We have a large project with C and C++ code.
For every C++ implementation, apart from the C++ header, we usually have provide a C-header to allow functionality to be available for .c files, also.
So, most of our files look like so:
foo.hpp:
class C {
int foo();
};
foo.h:
#ifdef __cplusplus
extern "C" {
typedef struct C C; // forward declarations
#else
class C;
#endif
int foo( C* ); // simply exposes a member function
C* utility_function( C* ); // some functionality *not* in foo.hpp
#ifdef __cplusplus
}
#endif
foo.cpp:
int C::foo() { /* implementation here...*/ }
extern "C"
int foo( C *p ) { return p->foo(); }
extern "C"
C* utility_function ( C* ) { /* implementation here...*/ }
QUESTION:
Suppose I wanted to add a namespace to the class like so:
foo.hpp:
namespace NS {
class C {
int foo();
};
}
what is the best scheme to follow in the C-headers?
I have considered a few options, but I'm looking for the most elegant, safe and easy to read. Is there a standard way you use?
Here are the options I've considered:
(I've ommitted the extern "C" constructs for simplicity)
- Option 1: fool the compiler by adding some code in each header:
foo.h
#ifdef __cplusplus
namespace NS { class C; } // forward declaration for C++
typedef NS::C NS_C;
#else
struct NS_C; // forward declaration for C
#endif
int foo( NS_C* );
NS_C* utility_function( NS_C* );
this adds some complexity to the header, but keeps the implementations unchanged.
Option 2: Wrap the namespace with a C-struct:
Keeps the header simple but makes the implementation more complex:
foo.h
struct NS_C; // forward declaration of wrapper (both for C++ and C)
int foo( NS_C* );
NS_C* utility_function( NS_C* );
foo.cpp
namespace NS {
int C::foo() { /* same code here */ }
}
struct NS_C { /* the wrapper */
NS::C *ptr;
};
extern "C"
int foo( NS_C *p ) { return p->ptr->foo(); }
extern "C"
NS_C *utility_function( NS_C *src )
{
NS_C *out = malloc( sizeof( NS_C ) ); // one extra malloc for the wrapper here...
out->ptr = new NS::C( src->ptr );
...
}
are these the only schemes? Are there any hidden disadvantages in any of these?
Csmells like undefined behaviour from miles away; but more importantly, I can't see the point: What is a C program going to do withfoo()? It can't have any usefulC-pointer around, so what's going on there?class Cwas implemented asstruct Cin a .c file. The .h file always contained thefoo(C*)functions. Clients of foo.h never knew the contents ofstruct C. It's encapsulation in 'C' terms. At some point,struct Cneeded to evolve and we changed it into aclass Cand moved its implementation in a .cpp file. No C program did anything with theCobject, ever. It always referenced theC-pointer though. This is typical for a >10 year old code. I hope this clarifies my intent.void*argument, and perform a cast in the C++ implementation: `inf foo(void * p) { return reinterpret_cast<C*>(p)->foo(); } The "never used" C struct type is just plain confusing and makes things worse. If you want a pointer that you never dereference, use a void pointer.void*can't. It is also more readable (at least if someone has explained this idiom). We have used both idioms and have settled to this one, because we found we had fewer mistakes and it reads better.