views:

696

answers:

6

I am using a 3rd party library that has a declaration like this:

typedef struct {} __INTERNAL_DATA, *HandleType;

And I'd like to create a class that takes a HandleType in the constructor:

class Foo
{
    Foo(HandleType h);
}

without including the header that defines HandleType. Normally, I'd just forward-declare such a type, but I can't figure out the syntax for this. I really want to say something like:

struct *HandleType;

But that says "Expected identifier before *" in GCC. The only solution I can see is to write my class like this:

struct __INTERNAL_DATA;
class Foo
{
    Foo(__INTERNAL_DATA *h);
}

But this relies on internal details of the library. That is to say, it uses the name __INTERNAL_DATA, which is an implementation detail.

It seems like it should be possible to forward-declare HandleType (part of the public API) without using __INTERNAL_DATA (part of the implementation of the library.) Anyone know how?

EDIT: Added more detail about what I'm looking for.

+3  A: 

Update:

I am using it in the implementation .cpp of Foo, but I want to avoid including it in my header .h for Foo. Maybe I'm just being too pedantic? :)

Yes you are :) Go ahead with forward declaration.

If HandleType is part of the interface there must be a header declaring that. Use that header.

Your problem is still a vague one. You are trying to protect against something you cannot.

You can add the following line to your client library:

typedef struct INTERNAL_DATA *HandleType;

but, if the name/structure changes you may be in for some casting nastiness.

Try templates:

template <class T>
class Foo
{
    Foo(T h);
};

Forward declaration is fine. If you are going to use pointers or references you only need a class (__INTERNAL_DATA) declaration in scope. However, if you are going to use a member function or an object you will need to include the header.

dirkgently
but then I need to forward-declare __INTERNAL_DATA, which is a name that could change or be removed altogether. (Since it's an implementation detail of the library.)
Jesse Rusak
So could HandleType for all you know.
dirkgently
Your requirements are not clear, maybe you can add some more detail.
dirkgently
HandleType is part of the public interface, though. I'm supposed to be using it. __INTERNAL_DATA isn't.
Jesse Rusak
I've added some more detail.
Jesse Rusak
So you can use the header that declares HandleType, right?
dirkgently
I am using it in the implementation .cpp of Foo, but I want to avoid including it in my header .h for Foo. Maybe I'm just being too pedantic? :)
Jesse Rusak
i think you cannot avoid using __INTERNAL_DATA here. you have to use a name that has linkage, and in C++ that is the first name in the typedef that is defined to be an alias for the struct (which is __INTERNAL_DATA) if the struct is unnamed as in your case.
Johannes Schaub - litb
@litb You're probably right.
Jesse Rusak
What's wrong with void*?
jmucchiello
@oe_mucchiello: Casts.
dirkgently
Casts take up no processing time. They syntax sugar for the compilers. You can't have both obscurement and transparency. Either you have to do a lot of annoying to read casts or you have to declare the internal structure where everyone can see.
jmucchiello
A: 

Note, that if you are not including the header, you are more susceptible to the risk of somebody changing the interface of the library. I don't see, how can you be after that. If you want to be independant of the library, you can create something like abstraction layer, but it is really a mess...

stanch
+1  A: 
 typedef struct {} __INTERNAL_DATA, *HandleType;

If it is defined like that (all on one line), then __INTERNAL DATA is as much a part of the public interface as HandleType.

However, I don't think __INTERNAL_DATA actually exists. More than likely, HandleType is really (internally) an int. This odd definition is just a way of defining it so that it's the same size as an int, but distinct, so that the compiler give you an error if you try passing an int where you're supposed to pass an HandleType. The library vendor could just as easily have defined it as "int" or "void*", but this way we get some type checking.

Hence __INTERNAL_DATA is just a convention and is not going to change.


UPDATE: The above was a bit of a mental burp... OK, __INTERNAL_DATA definitely does not exist. We know this for a fact, because we can see it's definition as an empty struct. I'm going to guess that the 3rd-party library uses "C" external linkage (no name managling), in which case, just copy the typedef -- it will be fine.

Inside the library itself, HandleType will have a completely different definition; maybe int, maybe "struct MyStruct {.......} *".

James Curran
+1  A: 

If the type is in a 3rd party library then the big benefit of forward declaration (isolating rebuilds due to changes in headers) is effectively lost.

If you're worried about compilation times (it's a sizeable header) then perhaps you can place it in a precompiled header or just include the relevant header from a library.

E.g. many library headers look like

// library.h
#include "Library/Something.h"
#include "Library/SomethingElse.h"
Andrew Grant
+1  A: 

I'm not quite sure what you're going for, but the following will work without including the actual header file:

// foo.h
class Foo
{
    public:
    template<typename T>Foo(T* h) { /* body of constructor */ }
};

Mind you, you will still have to have access the public members of __INTERNAL_DATA within the body of the constructor.

edit: as pointed out by James Curran, the __INTERNAL_DATA structure has no members, so it can be used, as above, with no problems.

e.James
Check the typedef closely... __INTERNAL_DATA has NO public (or private) members....
James Curran
I assumed that was just an example structure, to keep the question simple.
e.James
...but I see your point. Answer updated.
e.James
+1  A: 

If you really, really, really don't want to expose _INTERNAL_DATA to the caller then your only real choice is to use typedef void* HandleType; Then inside your library you can do anything you want including changing the entire implementation of *HandleType.

Just create a helper function to access you real data.

inline _INTERNAL_DATA* Impl(HandleType h) {
    return static_cast<_INTERNAL_DATA*>(h);
}
jmucchiello