creating with new and destroying with free

Discussion in 'C++' started by Bartholomew Simpson, Jun 4, 2007.

  1. I am writing some C++ wrappers around some legacy C ones - more
    specifically, I am providing ctors, dtors and assignment operators for
    the C structs.

    I have a ton of existing C code that uses these structs. A typical usage
    case will be as ff (note the code below is Pseudocode and WILL NOT compile)

    //example structs (I have left out the ctors/dtors etc for brevity sake)

    struct MyStructA
    {
    char name[4];
    long x;
    long y ;
    int z ;
    };


    struct MyStructB
    {
    long a ;
    long b ;
    MyStructA * struct_array ;
    long allocedsize ;
    long numstructs ;
    char key[16];
    };


    struct MyStructC
    {
    long g ;
    long h ;
    MyStructB * struct_array ;
    long allocedsize ;
    long numstructs ;
    };



    //example C API funcs (ignore null ptrs for now)

    void foo_addB(MyStructC* p1, MyStructB* p2)
    {
    //logic to determine if there is enough room in the
    // array for a new struct, if not malloc some more, then
    // get position (pos) in array of new struct
    memcpy(&p1->struct_array[pos], p2, sizeof(MyStructB));
    }


    void foo_removeB(MyStructC* p1, const size_t pos)
    {
    //logic to validate speicified pos (return if !valid)
    free(&p1->struct_array[pos]) ;
    p1->struct_array[pos] =0 ;
    }



    //C++ code
    bool SomeClass::Method1(MyStructC* arg)
    {
    //object created on stack
    MyStructB b ; //C++ default ctor called (code not shown)

    //calls C function
    foo_addB( arg, &b) ;
    }



    bool SomeClass::Method2(MyStructC* arg)
    {
    //object storage alloced from heap
    MyStructB * b = new MyStructB() ; //C++ default ctor called (code
    not shown)

    //calls C function
    foo_addB( arg, b) ;
    }


    bool SomeClass::Method3(MyStructC* arg)
    {
    //object storage alloced from heap
    MyStructB * b = reinterpret_cast<MyStructB*>(calloc( 1, MyStructB())) ;

    //calls C function
    foo_addB( arg, b) ;
    }




    Notes:
    ======
    Because the assignments in the C code are "shallow" copies (C struct
    assignment), the copy assignement operators for the structs are also
    "shallow", though I have provided explicit methods on the structs to
    facilitate "deep" copying.


    I have the ff questions:
    ==========================
    Bearing in mind that the C API only carries out a 'shallow' copy which
    of the 3 C++ methods above are correct (atleast in the sense that they
    will not leak memory, and will not attempt to delete memory twice)?


    Here are my observersations/thoughts so far:

    SomeClass::Method1()
    This will not leak memory (created on stack), but when it goes out of
    memory, when foo_removeB() is called, we will be attempting to free an
    invalid ptr (also, I suspect, the variable was implicity created by new
    behind the scenes - if this is the case trhen there is the [potential?]
    issue of destroying with free, an object created with new() ).

    SomeClass::Method2()
    This will no doubt leak memory - since I am not deleting after the new,
    also, there is the issue of destroying an object created with new, by
    calling free

    SomeClass::Method3()
    This seems to be the "best" solution in that memory is alloced from the
    heap, and we explicitly specify calloc - so (hopefully), calling free on
    this pointer should be ok?


    My questions are
    ==================
    i). Is my thinking (so far) correct?
    ii). Is there anything I may be overlooking ?
    iii). What is the best way to create a struct in C++ code and pass to
    the C API functions foo_addB() and foo_removeB()?
     
    Bartholomew Simpson, Jun 4, 2007
    #1
    1. Advertising

  2. Bartholomew Simpson

    Bo Persson Guest

    Bartholomew Simpson wrote:
    :: I am writing some C++ wrappers around some legacy C ones - more
    :: specifically, I am providing ctors, dtors and assignment operators
    :: for
    :: the C structs.

    And that makes them C++ struct. See below!

    ::
    :: I have a ton of existing C code that uses these structs. A typical
    :: usage case will be as ff (note the code below is Pseudocode and
    :: WILL NOT compile)
    ::
    :: //example structs (I have left out the ctors/dtors etc for brevity
    :: sake)
    ::
    :: struct MyStructA
    :: {
    :: char name[4];
    :: long x;
    :: long y ;
    :: int z ;
    :: };
    ::
    ::
    :: struct MyStructB
    :: {
    :: long a ;
    :: long b ;
    :: MyStructA * struct_array ;
    :: long allocedsize ;
    :: long numstructs ;

    The three lines above is pretty much what a std::vector is. Why not
    consider using one?

    :: char key[16];
    :: };
    ::
    ::
    :: struct MyStructC
    :: {
    :: long g ;
    :: long h ;
    :: MyStructB * struct_array ;
    :: long allocedsize ;
    :: long numstructs ;

    Here's another std::vector, I guess.

    :: };
    ::
    ::
    ::
    :: //example C API funcs (ignore null ptrs for now)
    ::
    :: void foo_addB(MyStructC* p1, MyStructB* p2)
    :: {
    :: //logic to determine if there is enough room in the
    :: // array for a new struct, if not malloc some more, then
    :: // get position (pos) in array of new struct
    :: memcpy(&p1->struct_array[pos], p2, sizeof(MyStructB));

    Here you get into trouble. If your struct has a constructor or a
    destructor, it is no longer memcpy compatible.

    :: }
    ::
    ::
    :: void foo_removeB(MyStructC* p1, const size_t pos)
    :: {
    :: //logic to validate speicified pos (return if !valid)
    :: free(&p1->struct_array[pos]) ;

    Who is calling the destructor for this object?


    :: p1->struct_array[pos] =0 ;
    :: }
    ::
    ::
    ::
    :: //C++ code
    :: bool SomeClass::Method1(MyStructC* arg)
    :: {
    :: //object created on stack
    :: MyStructB b ; //C++ default ctor called (code not shown)
    ::
    :: //calls C function
    :: foo_addB( arg, &b) ;
    :: }
    ::
    ::
    ::
    :: bool SomeClass::Method2(MyStructC* arg)
    :: {
    :: //object storage alloced from heap
    :: MyStructB * b = new MyStructB() ; //C++ default ctor called
    :: (code
    :: not shown)
    ::
    :: //calls C function
    :: foo_addB( arg, b) ;
    :: }
    ::
    ::
    :: bool SomeClass::Method3(MyStructC* arg)
    :: {
    :: //object storage alloced from heap
    :: MyStructB * b = reinterpret_cast<MyStructB*>(calloc( 1,
    :: MyStructB())) ;

    This really doesn't work at all.

    ::
    :: //calls C function
    :: foo_addB( arg, b) ;
    :: }
    ::
    ::
    ::
    ::
    :: Notes:
    :: ======
    :: Because the assignments in the C code are "shallow" copies (C
    :: struct assignment), the copy assignement operators for the structs
    :: are also "shallow", though I have provided explicit methods on the
    :: structs to facilitate "deep" copying.

    If you want shallow copies, you don't have to write the assignment
    operator. The compiler will do that for you, as a default.

    ::
    ::
    :: I have the ff questions:
    :: ==========================
    :: Bearing in mind that the C API only carries out a 'shallow' copy
    :: which
    :: of the 3 C++ methods above are correct (atleast in the sense that
    :: they will not leak memory, and will not attempt to delete memory
    :: twice)?
    ::
    ::
    :: Here are my observersations/thoughts so far:
    ::
    :: SomeClass::Method1()
    :: This will not leak memory (created on stack), but when it goes out
    :: of memory, when foo_removeB() is called, we will be attempting to
    :: free an invalid ptr (also, I suspect, the variable was implicity
    :: created by new behind the scenes - if this is the case trhen there
    :: is the [potential?] issue of destroying with free, an object
    :: created with new() ).

    No, there is no new calls behind the scene. The object is constructed
    on the stack, and destructed at the end of the function.

    As you make a copy the object (if memcpy were allowed), you free the
    copy, not the original.

    There might be a problem though with the shallow copy, but as we don't
    know what struct_array points to, we cannot be sure how it should be
    handled.

    ::
    :: SomeClass::Method2()
    :: This will no doubt leak memory - since I am not deleting after the
    :: new, also, there is the issue of destroying an object created
    :: with new, by calling free

    You are actually free'ing a copy (if only memcpy was allowed :).

    ::
    :: SomeClass::Method3()
    :: This seems to be the "best" solution in that memory is alloced
    :: from the heap, and we explicitly specify calloc - so (hopefully),
    :: calling free on this pointer should be ok?

    I think you are trying too hard here.

    If you want to do this the C++ way, and don't HAVE to keep the C API,
    you could try storing the objects in a std::vector member of the
    struct. Then you just do v.push_back(object), and the storage and the
    construction/destruction will be managed automagically. That's it!

    ::
    ::
    :: My questions are
    :: ==================
    :: i). Is my thinking (so far) correct?
    :: ii). Is there anything I may be overlooking ?
    :: iii). What is the best way to create a struct in C++ code and pass
    :: to the C API functions foo_addB() and foo_removeB()?

    If you really need a C struct, you have to keep it a C struct. If you
    add C++ features to it, it is a C++ struct (and incompatible).


    Bo Persson
     
    Bo Persson, Jun 4, 2007
    #2
    1. Advertising

  3. Bo Persson wrote:

    > Bartholomew Simpson wrote:
    > :: I am writing some C++ wrappers around some legacy C ones - more
    > :: specifically, I am providing ctors, dtors and assignment operators
    > :: for
    > :: the C structs.
    >
    > And that makes them C++ struct. See below!
    >
    > ::
    > :: I have a ton of existing C code that uses these structs. A typical
    > :: usage case will be as ff (note the code below is Pseudocode and
    > :: WILL NOT compile)
    > ::
    > :: //example structs (I have left out the ctors/dtors etc for brevity
    > :: sake)
    > ::
    > :: struct MyStructA
    > :: {
    > :: char name[4];
    > :: long x;
    > :: long y ;
    > :: int z ;
    > :: };
    > ::
    > ::
    > :: struct MyStructB
    > :: {
    > :: long a ;
    > :: long b ;
    > :: MyStructA * struct_array ;
    > :: long allocedsize ;
    > :: long numstructs ;
    >
    > The three lines above is pretty much what a std::vector is. Why not
    > consider using one?
    >


    Ah, if life were but that simple. The C structs are much hairier than
    that (in that they are nested to several layers), I simply posted some
    simple structs hre, since posting the original structs in all their gory
    detail would only serve as a red herring and simply confuse the core issue.

    > :: char key[16];
    > :: };
    > ::
    > ::
    > :: struct MyStructC
    > :: {
    > :: long g ;
    > :: long h ;
    > :: MyStructB * struct_array ;
    > :: long allocedsize ;
    > :: long numstructs ;
    >
    > Here's another std::vector, I guess.
    >
    > :: };
    > ::
    > ::
    > ::
    > :: //example C API funcs (ignore null ptrs for now)
    > ::
    > :: void foo_addB(MyStructC* p1, MyStructB* p2)
    > :: {
    > :: //logic to determine if there is enough room in the
    > :: // array for a new struct, if not malloc some more, then
    > :: // get position (pos) in array of new struct
    > :: memcpy(&p1->struct_array[pos], p2, sizeof(MyStructB));
    >
    > Here you get into trouble. If your struct has a constructor or a
    > destructor, it is no longer memcpy compatible.


    Hello!, this IS news to me ..... *shakes head in disbelief*

    >
    > :: }
    > ::
    > ::
    > :: void foo_removeB(MyStructC* p1, const size_t pos)
    > :: {
    > :: //logic to validate speicified pos (return if !valid)
    > :: free(&p1->struct_array[pos]) ;
    >
    > Who is calling the destructor for this object?
    >
    >


    Erm, its a C API library function - there is no "owner" as such ... its
    just a function in the library. Theooretically, it can be called by any
    func that supplies the correct args, though in practice, it is only used
    within a graphics library.

    > :: p1->struct_array[pos] =0 ;
    > :: }
    > ::
    > ::
    > ::
    > :: //C++ code
    > :: bool SomeClass::Method1(MyStructC* arg)
    > :: {
    > :: //object created on stack
    > :: MyStructB b ; //C++ default ctor called (code not shown)
    > ::
    > :: //calls C function
    > :: foo_addB( arg, &b) ;
    > :: }
    > ::
    > ::
    > ::
    > :: bool SomeClass::Method2(MyStructC* arg)
    > :: {
    > :: //object storage alloced from heap
    > :: MyStructB * b = new MyStructB() ; //C++ default ctor called
    > :: (code
    > :: not shown)
    > ::
    > :: //calls C function
    > :: foo_addB( arg, b) ;
    > :: }
    > ::
    > ::
    > :: bool SomeClass::Method3(MyStructC* arg)
    > :: {
    > :: //object storage alloced from heap
    > :: MyStructB * b = reinterpret_cast<MyStructB*>(calloc( 1,
    > :: MyStructB())) ;
    >
    > This really doesn't work at all.


    Why?. (I suppose reinterpret_cast does not like memory blocks not
    alloc'd by new()?) I suppose its back to old C style casts then ...

    >
    > ::
    > :: //calls C function
    > :: foo_addB( arg, b) ;
    > :: }
    > ::
    > ::
    > ::
    > ::
    > :: Notes:
    > :: ======
    > :: Because the assignments in the C code are "shallow" copies (C
    > :: struct assignment), the copy assignement operators for the structs
    > :: are also "shallow", though I have provided explicit methods on the
    > :: structs to facilitate "deep" copying.
    >
    > If you want shallow copies, you don't have to write the assignment
    > operator. The compiler will do that for you, as a default.
    >


    Thanks for the reminder

    > ::
    > ::
    > :: I have the ff questions:
    > :: ==========================
    > :: Bearing in mind that the C API only carries out a 'shallow' copy
    > :: which
    > :: of the 3 C++ methods above are correct (atleast in the sense that
    > :: they will not leak memory, and will not attempt to delete memory
    > :: twice)?
    > ::
    > ::
    > :: Here are my observersations/thoughts so far:
    > ::
    > :: SomeClass::Method1()
    > :: This will not leak memory (created on stack), but when it goes out
    > :: of memory, when foo_removeB() is called, we will be attempting to
    > :: free an invalid ptr (also, I suspect, the variable was implicity
    > :: created by new behind the scenes - if this is the case trhen there
    > :: is the [potential?] issue of destroying with free, an object
    > :: created with new() ).
    >
    > No, there is no new calls behind the scene. The object is constructed
    > on the stack, and destructed at the end of the function.
    >
    > As you make a copy the object (if memcpy were allowed), you free the
    > copy, not the original.
    >
    > There might be a problem though with the shallow copy, but as we don't
    > know what struct_array points to, we cannot be sure how it should be
    > handled.
    >
    > ::
    > :: SomeClass::Method2()
    > :: This will no doubt leak memory - since I am not deleting after the
    > :: new, also, there is the issue of destroying an object created
    > :: with new, by calling free
    >
    > You are actually free'ing a copy (if only memcpy was allowed :).


    I'm not sure I follow you ... memcpy DOES NOT free memory after copying
    a block of memory ... so I don't understand what youre saying - please
    elaborate further.

    >
    > ::
    > :: SomeClass::Method3()
    > :: This seems to be the "best" solution in that memory is alloced
    > :: from the heap, and we explicitly specify calloc - so (hopefully),
    > :: calling free on this pointer should be ok?
    >
    > I think you are trying too hard here.
    >
    > If you want to do this the C++ way, and don't HAVE to keep the C API,
    > you could try storing the objects in a std::vector member of the
    > struct. Then you just do v.push_back(object), and the storage and the
    > construction/destruction will be managed automagically. That's it!
    >


    I HAVE to use the C library - that is the whole point. I need to find a
    SAFE way of creating the structs in C++ and then passing them to C
    libray functions that will directly access the nested structures and on
    occasion, free memory (ptr to nested struct) that was allocated in the
    C++ code - it sounds more complicated than it really is - this kind of
    stuff is done in C all the time, though it may seem fiddly to "pure" C++
    programmers. I really need advice from someone who has a strong
    background in C as well (as opposed to C++ only) - the reason I posted
    in this ng rather than the comp.lang.c ng is that the probs I am having
    are from the C++ end - the C++ library works fine (has been for the last
    10yrs or so).

    > ::
    > ::
    > :: My questions are
    > :: ==================
    > :: i). Is my thinking (so far) correct?
    > :: ii). Is there anything I may be overlooking ?
    > :: iii). What is the best way to create a struct in C++ code and pass
    > :: to the C API functions foo_addB() and foo_removeB()?
    >
    > If you really need a C struct, you have to keep it a C struct. If you
    > add C++ features to it, it is a C++ struct (and incompatible).
    >


    Hmm, this is simply not true

    >
    > Bo Persson
    >
    >
     
    Bartholomew Simpson, Jun 4, 2007
    #3
  4. Bartholomew Simpson

    Bo Persson Guest

    Bartholomew Simpson wrote:
    :: Bo Persson wrote:
    ::
    ::: Bartholomew Simpson wrote:
    ::::: I am writing some C++ wrappers around some legacy C ones - more
    ::::: specifically, I am providing ctors, dtors and assignment
    ::::: operators for
    ::::: the C structs.
    :::
    ::: And that makes them C++ struct. See below!
    :::
    :::::
    ::::: I have a ton of existing C code that uses these structs. A
    ::::: typical usage case will be as ff (note the code below is
    ::::: Pseudocode and WILL NOT compile)
    :::::
    ::::: //example structs (I have left out the ctors/dtors etc for
    ::::: brevity sake)
    :::::
    ::::: struct MyStructA
    ::::: {
    ::::: char name[4];
    ::::: long x;
    ::::: long y ;
    ::::: int z ;
    ::::: };
    :::::
    :::::
    ::::: struct MyStructB
    ::::: {
    ::::: long a ;
    ::::: long b ;
    ::::: MyStructA * struct_array ;
    ::::: long allocedsize ;
    ::::: long numstructs ;
    :::
    ::: The three lines above is pretty much what a std::vector is. Why
    ::: not consider using one?
    :::
    ::
    :: Ah, if life were but that simple. The C structs are much hairier
    :: than
    :: that (in that they are nested to several layers), I simply posted
    :: some simple structs hre, since posting the original structs in all
    :: their gory detail would only serve as a red herring and simply
    :: confuse the core issue.

    Ok...

    I'm just saying that std::vector has size(), capacity(), and a buffer.
    If you don't HAVE TO manage this by hand, just don't!

    :::::
    ::::: //example C API funcs (ignore null ptrs for now)
    :::::
    ::::: void foo_addB(MyStructC* p1, MyStructB* p2)
    ::::: {
    ::::: //logic to determine if there is enough room in the
    ::::: // array for a new struct, if not malloc some more, then
    ::::: // get position (pos) in array of new struct
    ::::: memcpy(&p1->struct_array[pos], p2, sizeof(MyStructB));
    :::
    ::: Here you get into trouble. If your struct has a constructor or a
    ::: destructor, it is no longer memcpy compatible.
    ::
    :: Hello!, this IS news to me ..... *shakes head in disbelief*

    You're in for a treat! :)

    The short version is that memcpy is a C style function that works for
    C style objects. That's it.

    The long version is that C++ is just different (better :). As soon as
    you add C++ features to your struct, it is equivalent to a class, and
    must follow class rules. C++ defines C data types as PODs
    (plain-old-data). PODs follow C rules, non-PODs do not have to.

    Starting out with section 8.5.1 of the C++ Standard:

    "An aggregate is an array or class (clause 9) with no user declared
    constructors (12.1), no private or protected non-static data members
    (clause 11), no base classes (clause 10), and no virtual functions
    (10.3)."

    And then in section 9, Classes:

    "A POD-struct is an aggregate class that has no non-static data
    members of type non-POD-struct, non-POD-union (or array of such types)
    or reference, and has no user-defined copy assignment operator and no
    user defined destructor."

    So, in C++ you can do a lot more than in C. But as soon as you do, you
    must follow the new rules!


    :::::
    ::::: bool SomeClass::Method3(MyStructC* arg)
    ::::: {
    ::::: //object storage alloced from heap
    ::::: MyStructB * b = reinterpret_cast<MyStructB*>(calloc( 1,
    ::::: MyStructB())) ;
    :::
    ::: This really doesn't work at all.
    ::
    :: Why?. (I suppose reinterpret_cast does not like memory blocks not
    :: alloc'd by new()?) I suppose its back to old C style casts then ...

    NO! No C-style casts! :)

    reinterpret_cast<> is fine (as a last resort) when doing tricky
    things. The problem here is that you would have to start out with

    calloc(1, sizeof(MyStructB))

    to get the proper size. But you will still have the problem of "who
    calls the constructor". The C++ "new"-statement was invented to solve
    this problem - it allocates space AND constructs an object in that
    space. The calloc function allocates space, and fills it with zero.
    Who says an object is all zero?!

    :::::
    ::::: SomeClass::Method2()
    ::::: This will no doubt leak memory - since I am not deleting after
    ::::: the new, also, there is the issue of destroying an object
    ::::: created with new, by calling free
    :::
    ::: You are actually free'ing a copy (if only memcpy was allowed :).
    ::
    :: I'm not sure I follow you ... memcpy DOES NOT free memory after
    :: copying
    :: a block of memory ... so I don't understand what youre saying -
    :: please elaborate further.

    memcpy creates a copy of (the bits of) the object. The space allocated
    to struct_array is free'ed later (I guess). I just say that copying a
    C++ object with memcpy isn't guaranteed to work. THAT is the problem -
    you are not logically copying the object, only its bits. This is where
    C++ differs from C...


    ::
    :::
    :::::
    ::::: SomeClass::Method3()
    ::::: This seems to be the "best" solution in that memory is alloced
    ::::: from the heap, and we explicitly specify calloc - so
    ::::: (hopefully), calling free on this pointer should be ok?
    :::
    ::: I think you are trying too hard here.
    :::
    ::: If you want to do this the C++ way, and don't HAVE to keep the C
    ::: API, you could try storing the objects in a std::vector member of
    ::: the struct. Then you just do v.push_back(object), and the storage
    ::: and the construction/destruction will be managed automagically.
    ::: That's it!
    :::
    ::
    :: I HAVE to use the C library - that is the whole point. I need to
    :: find a SAFE way of creating the structs in C++ and then passing
    :: them to C
    :: libray functions that will directly access the nested structures
    :: and on occasion, free memory (ptr to nested struct) that was
    :: allocated in the
    :: C++ code - it sounds more complicated than it really is - this
    :: kind of stuff is done in C all the time, though it may seem fiddly
    :: to "pure" C++ programmers. I really need advice from someone who
    :: has a strong
    :: background in C as well (as opposed to C++ only) - the reason I
    :: posted
    :: in this ng rather than the comp.lang.c ng is that the probs I am
    :: having
    :: are from the C++ end - the C++ library works fine (has been for
    :: the last 10yrs or so).

    The thing is that C compatible structs can (almost) only use C
    features. As soon as you add anything of what I quoted above - private
    or protected data, base classes, non-POD members, constructors,
    destructors, assignment operators, or virtual functions - you leave C
    and enter C++.

    That means no bitwise copy (memcpy), but copying objects (std::copy).
    You must also match allocation and deallocation - malloc/calloc/free,
    new/delete, new[]/delete[]. There are no exceptions to this, if you
    want your code to be portable. Sorry!


    ::
    :::::
    :::::
    ::::: My questions are
    ::::: ==================
    ::::: i). Is my thinking (so far) correct?
    ::::: ii). Is there anything I may be overlooking ?
    ::::: iii). What is the best way to create a struct in C++ code and
    ::::: pass to the C API functions foo_addB() and foo_removeB()?
    :::
    ::: If you really need a C struct, you have to keep it a C struct. If
    ::: you add C++ features to it, it is a C++ struct (and incompatible).
    :::
    ::
    :: Hmm, this is simply not true

    I believe it is, unfortunately.


    Bo Persson
     
    Bo Persson, Jun 4, 2007
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. molar
    Replies:
    0
    Views:
    619
    molar
    Jul 25, 2004
  2. Bushido Hacks

    Destroying and creating an object

    Bushido Hacks, May 3, 2005, in forum: C++
    Replies:
    17
    Views:
    636
    Bushido Hacks
    May 4, 2005
  3. Replies:
    8
    Views:
    343
    Neil Cerutti
    Dec 22, 2005
  4. Replies:
    0
    Views:
    247
  5. mohammed_a_o
    Replies:
    0
    Views:
    303
    mohammed_a_o
    Nov 30, 2010
Loading...

Share This Page