creating with new and destroying with free

  • Thread starter Bartholomew Simpson
  • Start date
B

Bartholomew Simpson

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()?
 
B

Bo Persson

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
 
B

Bartholomew Simpson

Bo said:
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
 
B

Bo Persson

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top