Reputation: 42939
I'm trying to interface a C++ class (e.g., class foo
) to C. What I have done so far is to define a C structure that holds an opaque pointer member variable (i.e., void*
), that points to the associated C++ foo
object.
struct C_foo {
void *foo_obj;
};
I defined an alloc()
C interface function that allocates objects of type C_foo
:
struct C_foo* alloc(/* input */) {
struct C_foo *out = new struct C_foo;
out->foo_obj = new foo(/* input */);
return out;
};
What I want to do now, is to create a dealloc()
C interface function that will properly deallocate objects of type C_foo
previously allocated with alloc()
shown above:
void dealloc(struct C_foo *obj) {
/* ??? */
}
I know that explicitly deleting the void*
pointer (i.e., delete obj->foo_obj;
) would results in undefined behaviour (§ 5.3.5/1 [expr.delete]):
The
delete
-expression’s result has typevoid
[81].[81] This implies that an object cannot be deleted using a pointer of type
void*
becausevoid
is not an object type.
How I'm going to properly deallocate a struct C_foo
object?
Upvotes: 3
Views: 6619
Reputation: 275976
I will assume that you always put an instance of the same type into that void*
.
In which case, pImpl
time:
struct foo_impl; // note, just a name
struct C_foo {
foo_impl *foo_obj; // can use pointers to undefined structs in both C and C++
};
now most of your problem goes away. C treats foo_obj
as an opaque pointer.
In C++ we include another header file (sample fields):
// in C++ **only** header file -- C does not see this:
struct foo_impl {
int x;
std::vector<double> v;
foo_impl( int, double const* b, double const* e ); // constructor
};
// functions exposed to C, but implemented in C++ with visibility of the above foo_impl
extern "C" struct C_foo* alloc(int x, double const* b, double const* e) {
struct C_foo *out = new struct C_foo;
out->foo_obj = new foo_impl(x, b, e);
return out;
};
extern "C" void dealloc(struct C_foo *obj) {
delete obj->foo_obj;
delete obj;
}
and you win.
Note that a struct
is just a name for a class
in C++ with default public
instead of default private
.
I changed the name from foo
to foo_impl
, and created some sample data in it.
If you could put more than one different kind of type into your void*
, I would first advise putting a pure-virtual interface class with a virtual destructor, and basically following the above steps.
Now, there are cases where you actually want to store more than one distinct, unrelated type in your opaque pointer. These are not that common. But in those cases, we will want to store a destroy function.
Again, prefer my above approach, but if it doesn't work, we have this one.
There are a few ways to store the deleter function:
typedef void(*foo_deleter)(void*);
struct C_foo {
void* foo_obj;
foo_deleter* deleter;
};
another approach is:
struct foo_impl;
struct C_foo {
foo_impl* foo_obj;
};
// elsewhere:
typedef void(*foo_deleter)(foo_impl*);
struct foo_impl {
foo_deleter* deleter;
};
template<typename T>
struct foo_details {
foo_impl header;
T* data;
~foo_details() { delete data; }
foo_details( T* in ):data(in) {}
foo_details( foo_details const& ) = delete;
foo_details& operator=( foo_details const& ) = delete;
foo_details():data(nullptr) { header.deleter=nullptr; }
};
then allocate a foo_details
to stick into the foo_obj
storing a foo
, reinterpret_cast
to a foo_impl
(valid under standard layout clauses), and store into foo_obj
.
The deleter
would then take a foo_impl
, reinterpret_cast
to a foo_details<foo>
and delete
.
To access the data, you'd have to figure out what type it is (you can stick extra type information in the foo_impl
, like an integer or whatever), then reinterpret_cast
to the appropriate foo_details<?>
and access the data
in it.
Realize that you'll need to be able to extract the type information of your opaque pointer somehow in order to use it: consider using whatever mechanism you use there to also determine how to delete it.
Upvotes: 1
Reputation: 5352
If you need to pass around opaque handles because of some C API, and your objects have totally disparate types, you can use an approach like the one outlined below.
Note that if your types all share a common base, you can just provide a virtual destructor for the base, static_cast
the void*
to a pointer to base, then delete
that. This is a much more common approach than the one I outline below.
This will need to hold a pointer to the object yuo allocated, and a pointer to something that encodes the type (so that you can delete it):
struct Handle {
void* handle;
void* deleter_info;
};
You'll have some classes; these are the things you want to pass around handles to instances of...
class Foo;
class Bar;
// etc
You'll also need a base class for your deleter:
struct deleter_base {
virtual void destroy(Handle h) = 0;
virtual ~deleter_base() {}
};
... and a class template to produce derived classes that know the relevant type:
template<typename T> struct deleter {
virtual void destroy(Handle h)
{
T* ptr = static_cast<T*>(h.handle);
delete ptr;
}
};
For each type you want to provide handles to, you'll need a function to create a handle:
Handle create_foo_handle()
{
Handle h = {0};
h.ptr = new foo;
h.deleter_info = new deleter<foo>;
return h;
}
Handle create_bar_handle()
{
Handle h = {0};
h.ptr = new bar;
h.deleter_info = new deleter<bar>;
return h;
}
You'll need a destroy function:
void destroy(Handle h)
{
deleter_base* deleter = static_cast<deleter_base*>(h.deleter_info);
deleter->destroy(h); // delete the foo, or bar, or whatever
delete deleter; // delete the deleter
}
The struct could hold a deleter_base* deleter_info
as opposed to void* deleter_info
. This is really a matter of taste, and whether you want a struct deleter_info;
in your C API. Storing it in a void*
hides the implementation details, making it truly opaque.
To be able to use the handle meaningfully, you'll also need to encode some other information to be able to retrieve something useful from the void* handle
member. Typically, variant types do this with an enum member. Alternatively, you could hope your users are smart enough to only pass their handles back to a function that expects a handle of the correct type. You could have different handle struct types (struct HandleFoo;
, struct HandleBar;
, ...) to enforce this, and still use void*
members internally to maintain opacity.
Upvotes: 5
Reputation: 21813
Create a common base class with a virtual destructor, and use that instead of void*.
You could do something like the following.
class GenericBase
{
public:
virtual ~GenericBase() = 0;
};
inline GenericBase::~GenericBase() {} // or put in source file without inline
template<class T>
class GenericWrapper : public GenericBase
{
public:
typedef T Type;
Type x;
GenericWrapper() {}
GenericWrapper(const Type& x) : x(x) {}
};
You can use dynamic_cast to convert GenericBase* to a concrete type, in order to benefit from safety-checking.
I just noticed you want to use this with C. Obviously you can't pass a GenericBase*
to C. But you can pass it to C as a void*
and cast back to GenericBase*
when you need to delete it.
Upvotes: 1
Reputation: 21013
Cast to the right type:
delete static_cast<foo*>(obj->foo_obj);
Upvotes: 3
Reputation: 254771
If you know (for sure) what type it points to, then cast:
delete static_cast<foo*>(obj->foo_obj);
If you've lost track of the type, then you'll need to redesign.
Upvotes: 14