Check to

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Travis Spencer

    Check to

    Hello,

    I am trying to expose a C++ class to C clients using a C-compatible wrapper
    API. While doing so, I hit a little snag. When a C client deletes one on my
    classes using the provided delete method, shouldn't the pointer that passed
    to DeleteFoo be null when returned?

    A simplified example of my project follows.

    Thanks a lot and enjoy the rest of the weekend.

    Regards,

    Travis Spencer
    Portland, OR. USA




    #include <iostream>

    typedef void* FOO_HANDLE;

    void DeleteFoo(FOO_H ANDLE f);

    class Foo { /* Compiler generated ctor and dtor */ };

    int main(int argc, char** argv)
    {
    // Foos are normally created by C clients using my CreateFoo API
    // function, but this is just a demo.
    FOO_HANDLE f = new Foo;

    DeleteFoo(f);

    if (f != NULL)
    std::cerr << "f != NULL. Darn!" << std::endl;
    else
    std::cout << "f == NULL. Cool!" << std::endl;
    }

    void DeleteFoo(FOO_H ANDLE f)
    {
    delete static_cast<Foo *>(f);
    f = NULL;
    }


  • Artie Gold

    #2
    Re: Check to

    Travis Spencer wrote:[color=blue]
    > Hello,
    >
    > I am trying to expose a C++ class to C clients using a C-compatible wrapper
    > API. While doing so, I hit a little snag. When a C client deletes one on my
    > classes using the provided delete method, shouldn't the pointer that passed
    > to DeleteFoo be null when returned?
    >
    > A simplified example of my project follows.
    >
    > Thanks a lot and enjoy the rest of the weekend.
    >
    > Regards,
    >
    > Travis Spencer
    > Portland, OR. USA
    >
    >
    >
    >
    > #include <iostream>
    >
    > typedef void* FOO_HANDLE;
    >
    > void DeleteFoo(FOO_H ANDLE f);
    >
    > class Foo { /* Compiler generated ctor and dtor */ };
    >
    > int main(int argc, char** argv)
    > {
    > // Foos are normally created by C clients using my CreateFoo API
    > // function, but this is just a demo.
    > FOO_HANDLE f = new Foo;
    >
    > DeleteFoo(f);
    >
    > if (f != NULL)
    > std::cerr << "f != NULL. Darn!" << std::endl;
    > else
    > std::cout << "f == NULL. Cool!" << std::endl;
    > }
    >
    > void DeleteFoo(FOO_H ANDLE f)[/color]

    ITYM:
    void DeleteFoo(FOO_H ANDLE& f)

    because otherwise,
    [color=blue]
    > {
    > delete static_cast<Foo *>(f);[/color]

    In the above statement, you're deleting the memory pointed to by the
    value of the argument `f' (which is local to the function).
    [color=blue]
    > f = NULL;[/color]

    You're now setting the value of this pointer (which is local to the
    function DeleteFoo()) to NULL.
    [color=blue]
    > }[/color]

    You need to accept a reference to the pointer if you want to change
    its value.
    [color=blue]
    >
    >[/color]
    HTH,
    --ag


    --
    Artie Gold -- Austin, Texas

    Comment

    • John Harrison

      #3
      Re: Check to


      "Travis Spencer" <travislspencer @hotmail.com> wrote in message
      news:birmab02jb p@enews2.newsgu y.com...[color=blue]
      > Hello,
      >
      > I am trying to expose a C++ class to C clients using a C-compatible[/color]
      wrapper[color=blue]
      > API. While doing so, I hit a little snag. When a C client deletes one on[/color]
      my[color=blue]
      > classes using the provided delete method, shouldn't the pointer that[/color]
      passed[color=blue]
      > to DeleteFoo be null when returned?
      >[/color]

      No because C and C++ use call by value. A function can never change its
      parameters in a way that is visible to the caller under any circumstances.
      This is because the function takes a copy of the parameters passed to it,
      even if the function changes a parameter, its only a copy of the one that
      was supplied by the caller.

      Consider

      int func(int x)
      {
      x = 2;
      }

      int main()
      {
      int y = 1;
      func(y);
      cout << y; // outputs 1
      }

      This is basic C and C++. Your example involves pointers, my examples uses
      integers, but there is no difference. Using pointers you can changes what is
      pointed to, but that's a completely different situation.

      The C++ exception is when references are involved, then a reference to the
      oiginal parameter is taken, not a copy, so the function can change the
      original parameter.

      I don't think there is much point in doing what you are trying to do.
      Consider

      void DeleteFoo(FOO_H ANDLE f);

      class Foo { /* Compiler generated ctor and dtor */ };

      int main(int argc, char** argv)
      {
      // Foos are normally created by C clients using my CreateFoo API
      // function, but this is just a demo.
      FOO_HANDLE f = new Foo;
      FOO_HANDLE annoying_copy = f;
      DeleteFoo(f);

      if (annoying_copy != NULL)
      std::cerr << "copy != NULL. Darn!" << std::endl;
      else
      std::cout << "copy == NULL. Cool!" << std::endl;
      }

      even if you did make f NULL, there is no way you can touch annoying_copy.
      Assuming that your client is going to do things like call functions with
      FOO_HANDLE or put FOO_HANDLE into data structures there are always going to
      be copies the original FOO_HANDLE.

      The only way to do this cleanly is to make FOO_HANDLE a class which has a
      destructor. Then the destructor can sort itself out. Since you are
      programming for C clients this is not possible, but C programmers have this
      sort of headache all the time so they aren't going to mind.

      john


      Comment

      • Travis Spencer

        #4
        Re: Check to

        Hey Artie,
        [color=blue]
        > You need to accept a reference to the pointer if you want to change
        > its value.[/color]

        Before posting originally, I did try changing DeleteFoo from

        void DeleteFoo(FOO_H ANDLE f)

        to

        void DeleteFoo(FOO_H ANDLE* f)

        with the same result. Does it have to be a reference? Why wouldn't a
        pointer to a pointer work?

        Regards,

        Travis Spencer
        Portland, OR. USA


        Comment

        • John Harrison

          #5
          Re: Check to


          "Travis Spencer" <travislspencer @hotmail.com> wrote in message
          news:bit6mt01sb 2@enews3.newsgu y.com...[color=blue]
          > Hey Artie,
          >[color=green]
          > > You need to accept a reference to the pointer if you want to change
          > > its value.[/color]
          >
          > Before posting originally, I did try changing DeleteFoo from
          >
          > void DeleteFoo(FOO_H ANDLE f)
          >
          > to
          >
          > void DeleteFoo(FOO_H ANDLE* f)
          >
          > with the same result. Does it have to be a reference? Why wouldn't a
          > pointer to a pointer work?
          >[/color]

          It would work. Did you make the correct change to DeleteFoo as well?

          void DeleteFoo(FOO_H ANDLE* f)
          {
          delete static_cast<Foo *>(*f);
          *f = NULL;
          }

          Still think you are wasting your time however.

          john


          Comment

          • Travis Spencer

            #6
            Re: Check to

            Hey John,
            [color=blue]
            > Still think you are wasting your time however.[/color]

            This may be but if so how am I supposed to unit test and regression test
            DeleteFoo? What can I check to assure that it successfully deleted its
            memory. I was considering this snippet (using CppUnitLite):


            TEST(FooAPI, DeleteFoo)
            {
            HANDLE f = CreateFoo("deep dd");
            DeleteFoo(f);
            CHECK(p == NULL); // FAILS
            }

            Any suggestions would be great.


            --
            Regards,

            Travis Spencer
            Portland, OR. USA



            Comment

            • John Harrison

              #7
              Re: Check to


              "Travis Spencer" <travislspencer @hotmail.com> wrote in message
              news:bivumf0rk9 @enews1.newsguy .com...[color=blue]
              > Hey John,
              >[color=green]
              > > Still think you are wasting your time however.[/color]
              >
              > This may be but if so how am I supposed to unit test and regression test
              > DeleteFoo? What can I check to assure that it successfully deleted its
              > memory. I was considering this snippet (using CppUnitLite):
              >
              >
              > TEST(FooAPI, DeleteFoo)
              > {
              > HANDLE f = CreateFoo("deep dd");
              > DeleteFoo(f);
              > CHECK(p == NULL); // FAILS[/color]

              What is p? Did you mean f?
              [color=blue]
              > }
              >
              > Any suggestions would be great.
              >[/color]

              There is no way in standard C++ to tell if an arbitrary pointer is pointing
              at allocated or deleted memory. One pointer looks pretty much like another.

              Maybe there are third party heap management libraries that would give you
              the checks you need, but I don't know of any to recommend.

              The way I would handle this is not to use pointers, they're too dangerous
              for what you want. Why not make HANDLE an integer, which indexes an array of
              Foo pointers that is held internally by your API? That might give you a
              little more security because you can control how the integers are doled out.
              When a Foo object is deleted you set the entry in the array of pointers to
              NULL. That gives you an easy check to see if an object has been deleted, at
              least until you have to reuse that entry in the array of pointers.

              Something like this.

              static Foo* array_of_foo[1000];

              int CreateFoo()
              {
              int next_free_slot = ...;
              array_of_foo[next_free_slot] = new Foo;
              return next_free_slot;
              }

              void DeleteFoo(int foo)
              {
              delete array_of_foo[foo];
              array_of_foo[foo] = NULL;
              }

              bool HasBeenDeleted( int foo)
              {
              return array_of_foo[foo] == NULL;
              }

              Its not perfect, because sooner of later you will have to reuse integers
              that have been used before, but it should give you more control than using
              raw pointers.

              john


              Comment

              • Travis Spencer

                #8
                Re: Check to

                Hello Again John,
                [color=blue][color=green]
                > > TEST(FooAPI, DeleteFoo)
                > > {
                > > HANDLE f = CreateFoo("deep dd");
                > > DeleteFoo(f);
                > > CHECK(p == NULL); // FAILS[/color]
                >
                > What is p? Did you mean f?
                >[color=green]
                > > }[/color][/color]

                Did I say p? Ya, I meant f.
                [color=blue]
                > Maybe there are third party heap management libraries that would give you
                > the checks you need,[/color]

                I don't want to go there. That sounds like way too much trouble for this
                little project. Good to know that such things exist though.
                [color=blue]
                > The way I would handle this is not to use pointers, they're too dangerous
                > for what you want. Why not make HANDLE an integer, which indexes an array[/color]
                of[color=blue]
                > Foo pointers that is held internally by your API?[/color]

                That is so clever!
                [color=blue]
                > That gives you an easy check to see if an object has been deleted, at
                > least until you have to reuse that entry in the array of pointers.[/color]

                <snip>
                [color=blue]
                > Its not perfect, because sooner of later you will have to reuse integers
                > that have been used before,[/color]

                Do you mean until all the elements of the array are full of valid pointers
                (i.e., array_of_foo[0...999] != NULL). Or do you mean that some mechanism
                will need to be put in place to indicate that array_of_foo[52] is available
                because it's pointer was deleted (while array_of_foo[0...51] and
                array_of_foo[53...999] are not free for example). Would a more complex data
                structure like a hash table be appropriate in this situation? Or do I just
                need to create an array_of_foo class that will shell out available elements,
                create new storage space if its little pool is all used up, etc.

                Very neat idea. I think I bum this one off ya.

                --
                Regards,

                Travis Spencer
                Portland, OR. USA


                Comment

                • John Harrison

                  #9
                  Re: Check to

                  >[color=blue][color=green]
                  > > Its not perfect, because sooner of later you will have to reuse integers
                  > > that have been used before,[/color]
                  >
                  > Do you mean until all the elements of the array are full of valid pointers
                  > (i.e., array_of_foo[0...999] != NULL). Or do you mean that some mechanism
                  > will need to be put in place to indicate that array_of_foo[52] is[/color]
                  available[color=blue]
                  > because it's pointer was deleted (while array_of_foo[0...51] and
                  > array_of_foo[53...999] are not free for example).[/color]

                  Either, both, you know your application.
                  [color=blue]
                  > Would a more complex data
                  > structure like a hash table be appropriate in this situation?[/color]

                  Can't see the need for a hash table. A linked list might be appropriate. The
                  list contains the array slots that are currently free. Saves searching
                  through the array.
                  [color=blue]
                  > Or do I just
                  > need to create an array_of_foo class that will shell out available[/color]
                  elements,[color=blue]
                  > create new storage space if its little pool is all used up, etc.
                  >[/color]

                  Maybe, there are lots of variations.
                  [color=blue]
                  > Very neat idea. I think I bum this one off ya.
                  >[/color]

                  Be my guest.

                  john


                  Comment

                  Working...