tags:

views:

259

answers:

4

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?

+11  A: 

You want the PIMPL idiom.

anon
That name still gives me a little shudder, but I was still about to post the same thing. +1
Michael Myers
Link plz. Kthx bye.
JSBangs
Wouldn't going through pimpl/bridge be equivalent to extra dereference through the vtable?
Nikolai N Fetissov
Also http://en.wikipedia.org/wiki/Opaque_pointer.
Michael Myers
anon
@Nikolai If you want indirection (which is the root of the both PIMPL and virtual functions) I can't see any way of not paying the price.
anon
@Nikolai N Fetissov it's an extra data dereference, not a function pointer, so is better optimised on most systems.
Pete Kirkham
If he can't afford virtual functions on his DSP, I really doubt he can afford PIMPL.
Fred Larson
@Nikolai, yes, but that overhead is pretty minimal. Try it and see if it really slows things down that much. Since it's all hidden inside the class the class's clients won't need to change if you change it back, just be recompiled.
Omnifarious
@Fred: "afford" isn't quite the right word. It's an expansion of software development that I have yet to be comfortable with + accept its performance/memory/maintenance/testing costs.
Jason S
@Fred Virtuial functions imply a table. PIMPL implies single pointer. But actually, I suspect he CAN afford VFs.
anon
@Neil: "I can't see any way of not paying the price" -- I thought Microsoft had some magic something-or-other which they used for COM and IUnknown for their ATL framework, namely ATL_NO_VTABLE: http://msdn.microsoft.com/en-us/library/6h06t6s8%28VS.80%29.aspx
Jason S
But PIMPL also requires a dynamic memory allocation on construction, does it not?
Fred Larson
@Fred: using an abstract interface also requires dynamic allocation, since you can't directly instantiate the derived implementation class. Both approaches have similar (but not necessarily the same) costs.
Mike Seymour
@Mike: I can make a pointer or reference without a dynamic allocation.
Fred Larson
@Jason MS of course implement the compiler as well as the ATL library, which maked "magic" much easier for them to accomplish :-)
anon
(boy, this is one of those cases where this website could really use some forum functionality!)
Jason S
+2  A: 

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.

Omnifarious
Ugly is a understatement. If the object is long-live then paying the price of a new for an internal datastruct is better than this. If it is short lived then perhaps an optimized pool allocator for this object type. Or just live with some extra compile time, I'd rather pay that than introduce code like this or major runtime impacts!
tim
For a complete discussion, see http://www.gotw.ca/gotw/028.htm "The Fast Pimpl Idiom".And even though it is a scary thing, I have written a template version of what Omnifarious has done, in order to make it more safe and usable. Still scary though.
tony
This has a thread safety advantage over fast pimpl. Making fast pimpl thread safe kills a lot of its speed. My hack becomes really tricky and extremely error prone when the contents of the internal data structure are not PODs.
Omnifarious
A: 
Nikolai N Fetissov
A: 

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.

rmn