Originally Posted by
bubuche
Wow,you are going way too far . I am not talking of anything needing to recompile the whole program.
Then you don't understand the opaque pointer idiom.
Originally Posted by
bubuche
As it is, the opaque pointer to Bar is not completely opaque. It allows other compilation unit to know that "Bar is an alias for struct Foo".
So it allow other compilation units to know that there is a struct under the coat.
And what's wrong with that? The idea behind the opaque pointer idiom is that it hides the internals of a library from the user of the library. The main benefit of this is that the library maintainer can change the internals of the library at will, such that users of the library just need to obtain the updated library and (compile the library and) link to it. If the opaque pointer idiom was not used, a change to the internals of the library could require the user of the library to recompile their own code that uses the library, because the header of the library has changed, thus affecting their own code. Of course, as another benefit it also means that the library owner can release a header and library file without source such that users of the library will have a somewhat harder time reverse engineering the internals of the library.
Originally Posted by
bubuche
I just wanted to have it more opaque : "Bar is a type, and that's all you need to know".
What you are asking for is impossible. If it is a type, it must be either a built-in type, an enum type, a struct type, a union type, function type, a pointer type or array type derived from these types, etc. It cannot simply be "a type" in the sense of say, a type name as a template parameter in C++.
Originally Posted by
bubuche
You didn't mention it but I can also
- cast the parameter (a "Bar *") to the actual type I want (a "int *", a "FILE *" etc).
No, you cannot do that, unless you want to have undefined behaviour. From your understanding that not all pointers have the same size, you already know this.
Originally Posted by
bubuche
Ok, imagine you have a fantastic macro like this
SET_CODE_OF(int)
that generate functions returning a "int_set", functions taking a "int_set" as parameter and allowing you to add, remove, check if the set contains a specific int, etc. Yes, just like a template, but shhhh.
Now I ask you to create an opaque pointer to handle ids of clients. Or anything basically being a set of ints. What you want to do is something like this:
I have already described how you can do this if you want to use the opaque pointer idiom, i.e., make the internal struct a wrapper for your third party type and do function forwarding:
ids.h:
Code:
//guard headers
struct IDs;
typedef struct IDs IDs;
IDs *create_IDs();
void IDs_destroy(IDs *);
void IDs_add(IDs *, int);
void IDs_remove(IDs *, int);
//etc
ids.c:
Code:
//includes
SET_CODE_OF(int)
struct IDs
{
int_set *values;
};
IDs * create_IDs(void)
{
IDs * ids = malloc(sizeof(*ids));
if (ids)
{
ids->values = create_int_set();
}
return ids;
}
void IDs_destroy(IDs * ids)
{
int_set_destroy(ids->values);
free(ids);
}
void IDs_add(IDs * ids, int i)
{
int_set_add(ids->values, i);
}
void IDs_remove(IDs * ids, int i)
{
int_set_remove(ids->values, i);
}
//etc
If you don't want to do this because the wrapper does do a little more work than directly using the library generated by SET_CODE_OF(int), then ditch the opaque pointer idiom and expose the implementation:
Code:
ids.h:
//guard headers
SET_CODE_OF(int)
typedef int_set IDs;
IDs * create_IDs();
void IDs_destroy(IDs *);
void IDs_add(IDs *, int);
void IDs_remove(IDs *, int);
//etc
If you don't want to do this either, perhaps because you really don't want to expose the implementation (for the reasons why people use the opaque pointer idiom in the first place, as I mentioned above), then there's the use of void* as GReaper suggested, but without confusing it with the opaque pointer idiom:
ids.h:
Code:
//guard headers
typedef void IDs;
IDs *create_IDs();
void IDs_destroy(IDs *);
void IDs_add(IDs *, int);
void IDs_remove(IDs *, int);
//etc
ids.c:
Code:
//includes
SET_CODE_OF(int)
IDs * create_IDs() { return create_int_set(); }
void IDs_destroy(IDs * ids) { int_set_destroy(ids); }
void IDs_add(IDs * ids, int i) { int_set_add(ids, i); }
void IDs_remove(IDs * ids, int i) { int_set_remove(ids, i); }
//etc
This comes close to what you originally envisioned, except that it carries the negative point christop mentioned in post #8: it is not type safe since your library's interface deals with a void* rather than a type specific to the library, so in theory a user of your library is more likely to make a mistake and provide a pointer to something other than int_set (which they never see, like in the opaque pointer idiom). The other thing would be that if you want to work with int_set* rather than void* because you want to do something more sophisticated than function forwarding, you then need to cast or to have an explicit int_set* local variable, and this inconvenience will remain forever even if you revert to using your own internal struct.