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.