Typedef private struct prototype in source file

729 views Asked by At

In my class I have the need to keep a pointer to a structure which is defined in a library I use to implement it. Since this library is only used within the implementation file I would like to avoid including it in the header directly. At the same time I want to avoid polluting the namespace. Thus I would like to do:

/* HEADER */
class Foo {
    private:
        struct ImplementationDetail;
        ImplementationDetail * p;
};
/* SOURCE */
#include <Library.h>
using Foo::ImplementationDetail = Library::SomeStruct;

But this doesn't work, and I'm currently falling back on PIMPL:

/* HEADER */
class Foo {
    private:
        struct ImplementationDetail;
        ImplementationDetail * p_;
};
/* SOURCE */
#include <Library.h>
struct ImplementationDetail {
    Library::SomeStruct * realp_; 
}

Is there a way to avoid the double dereference? Is the reason for my non-working first solution due to unknown pointer sizes?

5

There are 5 answers

4
John Dibling On BEST ANSWER

This is an incorrect declaration:

using Foo::ImplementationDetail = Library::SomeStruct;

using doesn't work this way. In C++11 using cannot create an alias for a name in one namespace to a name in another namespace. In C++03, all using does is bring some other namespace in to global visibility in the current translation unit. It's not used to create aliases in C++03, as you seem to want to do here.

pimpl is the de-facto method for doing what you're trying to do, but in your header file instead of trying to use a ImplementationDetail*, I would use a simple void*. Using a void* in this manner is guaranteed to be correct according to the Standard:

class Foo {
    private:
        void * pImpl;

Use static_cast2 to go from a void* to your actual type:

void Foo::Bar()
{
  Library::SomeStruct* thingy = static_cast <Library::SomeStruct*> (pImpl);
  // ...
}

You can avoid using the void* in a conformant way by forward-declaring your library type:

namespace Library
{
  struct SomeStruct;
};

class Foo
{
private:
  Library::SomeStruct* pStruct;
};

And then no ugly cast is needed in the implementation.


2 Use static_cast : Or reinterpret_cast

1
Mark B On

The reason you can't take your first approach is that in the header you tell the compiler "I'm declaring a nested class within Foo and it's called ImplementationDetail". Then you proceed to say "wait wait, it's NOT a new class, it's an alias to this other thing entirely" and understandably the compiler gets confused.

Have you tried just forward declaring the library's implementation and using that instead of trying to create an alias?

0
Dietmar Kühl On

In your first code you declared the nested type ImplementationDetail to be a struct which will be define inside Foo. Trying to alias it can't work because that would be a type defined elsewhere and, actually, you private structure isn't accessible from outside the class. Wrapping a pointer to another object inside seems unecessary: you could instead either embed the Library::SomeStruct by value or have your ImplementationDetail derive from Library::SomeStruct:

struct ImplementationDetail
    : Library::SomeStruct {
    using Library::SomeStruct::SomeStruct;
};

(the using declaration is just used to inherit all the constructors from Library::SomeStruct).

1
code-factory On

I think this is not possible without casting.

Basically there are two ways to do it:

1) Define p_ as void* and cast it in every function that uses it.

/* HEADER */
class Foo {
    private:
        void* p;
};

/* SOURCE */
#include <Library.h>

void Foo::AnyFunc()
{
    Library::SomeStruct* pImpl = reinterpret_cast<Library::SomeStruct*>(p);
    ...
}

2) Create a "shadowing"-class of your class (in the .cpp-file) with all members cloned and p_ defined as Library::SomeStruct. Then cast the this-pointer to this shadowing class. This is of course a quite insecure and dirty hack which I don't recommend...

/* HEADER */
class Foo {
    private:
        void* p;
};

/* SOURCE */
#include <Library.h>

class FooImpl
{
public:
    void AnyFunc() { p->DoSomething(); }

private:
    Library::SomeStruct* p;
}

void Foo::AnyFunc()
{
    FooImpl* pImpl = reinterpret_cast<FooImpl*>(this);
    pImpl->AnyFunc();
}

This exploits memory structure and is therefore quite fragile (all members need to be in the same order and when you add or remove members, you need to update ShadowFoo, too). I mentioned this just for completeness.

3) This brings us to yet another, but more simple way: create the implementation in the source file and initialize it in the constructor with the void*-pointer:

/* HEADER */
class Foo {
    private:
        void* p;
};

/* SOURCE */
#include <Library.h>

class FooImpl
{
public:
    FooImpl(void* pSomeStruct)
    {
        p = reinterpret_cast<Library::SomeStruct*>(pSomeStruct);
    }

    void AnyFunc() { p->DoSomething(); }

private:
    Library::SomeStruct* p;
}

void Foo::AnyFunc()
{
    FooImpl impl = FooImpl(p);
    impl.AnyFunc();
}
1
AudioBubble On
// Header
class Foo {
    private:
        struct ImplementationDetail;
        ImplementationDetail * p;
};

// Source
#include <Library.h>
struct Foo::ImplementationDetail :public Library::SomeStruct {
   // ....
};

and allocating/deallocating/dereferencing the pointer in this source file only should work just fine.