Check to

T

Travis Spencer

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_HANDLE 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_HANDLE f)
{
delete static_cast<Foo*>(f);
f = NULL;
}
 
A

Artie Gold

Travis said:
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_HANDLE 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_HANDLE f)

ITYM:
void DeleteFoo(FOO_HANDLE& f)

because otherwise,
{
delete static_cast<Foo*>(f);

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

You're now setting the value of this pointer (which is local to the
function DeleteFoo()) to NULL.

You need to accept a reference to the pointer if you want to change
its value.
HTH,
--ag
 
J

John Harrison

Travis Spencer said:
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?

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_HANDLE 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
 
T

Travis Spencer

Hey Artie,
You need to accept a reference to the pointer if you want to change
its value.

Before posting originally, I did try changing DeleteFoo from

void DeleteFoo(FOO_HANDLE f)

to

void DeleteFoo(FOO_HANDLE* 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
 
J

John Harrison

Travis Spencer said:
Hey Artie,


Before posting originally, I did try changing DeleteFoo from

void DeleteFoo(FOO_HANDLE f)

to

void DeleteFoo(FOO_HANDLE* f)

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

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

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

Still think you are wasting your time however.

john
 
T

Travis Spencer

Hey John,
Still think you are wasting your time however.

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("deepdd");
DeleteFoo(f);
CHECK(p == NULL); // FAILS
}

Any suggestions would be great.
 
J

John Harrison

Travis Spencer said:
Hey John,


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("deepdd");
DeleteFoo(f);
CHECK(p == NULL); // FAILS

What is p? Did you mean f?
}

Any suggestions would be great.

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
 
T

Travis Spencer

Hello Again John,
What is p? Did you mean f?

Did I say p? Ya, I meant f.
Maybe there are third party heap management libraries that would give you
the checks you need,

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.
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 is so clever!
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.

Its not perfect, because sooner of later you will have to reuse integers
that have been used before,

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.
 
J

John Harrison

Its not perfect, because sooner of later you will have to reuse integers
that have been used before,

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).

Either, both, you know your application.
Would a more complex data
structure like a hash table be appropriate in this situation?

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.
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.

Maybe, there are lots of variations.
Very neat idea. I think I bum this one off ya.

Be my guest.

john
 

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

Staff online

Members online

Forum statistics

Threads
474,142
Messages
2,570,817
Members
47,363
Latest member
eitamoro

Latest Threads

Top