Reputation: 189686
I've been spoiled using Java in the last few months! I have a C++ project where I would like to decouple a class interface (.h file) from its implementation details. But the class's member fields have to be in its declaration, and it seems like I have this unavoidable dependency linking if I want to tweak the class's member fields.
I know one way to do this is using polymorphism + class inheritance (make the interface a base class, make the implementation a derived class), but if I remember right, that requires virtual functions, which are something I would like to avoid -- this is on a DSP and it's advantageous not to get too "C++-y" with things.
any suggestions?
Upvotes: 4
Views: 3057
Reputation: 2426
Use the pimpl idiom. Read here: http://www.devx.com/cplus/Article/28105/0/page/3
It will help decoupling the implementation from the interface and will reduce (to a minimum) all compilation dependencies. You can even avoid virtual functions.
Upvotes: 1
Reputation: 84169
Here's an old idea :) - opaque data type plus a set of functions, i.e. "there and back [to C] again":
// oi.hpp
namespace oi // old idea
{
struct opaque; // forward declaration
void init( opaque& ); // ctor
void fini( opaque& ); // dtor
int get_foo( const opaque& ); // getter
void set_foo( opaque&, int ); // setter
}
// oi.cpp
namespace oi
{
struct opaque // definition
{
int foo_; // data members
// ...
};
// function definitions
}
The runtime cost of accessing the structure via reference is probably the same as with pimpl, so this is probably an inferior solution given some important idioms like RAII cannot be used.
Upvotes: 0
Reputation: 56048
You know, I thought about this and your objection to PIMPL for a bit.
I have an ugly hack I use sometimes for cases like this, where I resent paying the indirection penalty. Though usually my complaint is with calling new, and not with the pointer dereference. I present my ugly hack thusly:
// IHaveOpaqueData.h
class IHaveOpaqueData {
public:
// To make sure there are no alignment problems, maybe ::std::uin64_t
typedef maximally_aligned_type_t internal_data_t[32]; // Some size I hope is big enough
void iCanHazMemberFunction();
// ...
private:
internal_data_t data;
};
// IHaveOpaqueData.cpp
#include <boost/static_assert.hpp>
namespace { // Hide it in an anonymous namespace
struct RealData {
int icanhazmembervariable_;
double icanhazdoublevariable_;
};
BOOST_STATIC_ASSERT(sizeof(RealData) < sizeof(IHaveOpaqueData::internal_data_t);
}
void IHaveOpaqueData::iCanHazMemberFunction()
{
// Use a reference to help the optimize make the right decision
RealData &datathis = *(reinterpret_cast<RealData *>(&(this->data)));
datathis.icanhazmembervariable_ = datathis.icanhazdoublevariable_;
}
Yes, this is ugly. BOOST_STATIC_ASSERT (or if you have a C++[01]x compiler the static_assert keyword) helps make it not be a total disaster. There may be a clever way to use unions to mitigate some of the twitchiness I have over alignment issues as well.
Upvotes: 2