Reputation: 1625
I'm trying to create a header-only C++ library around an external C API.
The C API uses void *
pointers as handles.
Here's the idea:
// resource.hpp
class Resource {
public:
// RAII constructor, destructor, etc.
// ...
void do_something(const Handle & h) {
do_something_impl( (void *) h);
}
};
// handle.hpp
class Handle
{
public:
Handle(size_t n, const Resource & res)
: p_(res.allocate(n)), res_(res) {}
// cast operation
operator void *() const { return p_; }
private:
void * p_;
Resource & res_;
};
The problem here is that (a) the Handle has to keep a reference to the Resource, and (b) the Resource needs to be able to cast the Handle to a void *. Unfortunately this leads to a circular dependency.
Any ideas on how to restructure this?
NOTE: The answer is not to simply "include xxx.hpp" or forward declare one of the classes. This needs to be restructured somehow, I just can't quite see how.
Adding a class Handle
as a forward declaration to the top of the Resource file doesn't work, because the (void *)
cast is part of the Handle definition that Resource still can't see. Likewise, changing the cast to a void * ptr()
member function leads to the same problem.
Moving the function definitions to a .cpp file is also not an answer -- it needs to be header-only.
Upvotes: 0
Views: 1629
Reputation: 1625
Well, it's templates to the rescue (AGAIN!):
// resource.hpp
class Resource;
template<typename TResource> class Handle;
class Resource {
public:
// RAII constructor, destructor, etc.
// ...
void do_something(const Handle<Resource> & h) {
do_something_impl( (void *) h);
}
};
// handle.hpp
template<class TResource>
class Handle {
public:
Handle(size_t n, const TResource & res)
: p_(res.allocate(n)), res_(res) {}
// cast operation
operator void *() const { return p_; }
private:
void * p_;
TResource & res_;
};
Upvotes: 4
Reputation: 409404
Don't include the header files into each other, instead you have a forward declaration the classes. This way they can be used as references or pointers.
So in resource.hpp:
class Handle;
class Resource {
public:
// RAII constructor, destructor, etc.
// ...
void do_something(const Handle & h) {
do_something_impl( (void *) h);
}
};
And in handle.hpp:
class Resource;
class Handle
{
public:
Handle(size_t n, const Resource & res)
: p_(res.allocate(n)), res_(res) {}
// cast operation
operator void *() const { return p_; }
private:
void * p_;
Resource & res_;
};
Since you use the void*
typecasting operator inside the do_something
function, you have to move that implementation into a separate source file. In that source file you can include both header files, so all functions can be accessed.
Upvotes: 1