Reputation: 2281
I'm developing a software which is in C++ but that communicates with a C app through a shared header file containing the communication protocol. Since C is "more basic" than C++, I always need to write the header in C code (so C++ also gets it); otherwise it wouldn't work for the second app.
The problem is that I need to use a scope-quilifier such as the C++ namespace
s - which don't exist in C.
What are all the options to emulate the namespace
feature in C?
The only possibility I have seen so far is the one shown in this SO question, but unfortunately the answer is not clear enough and I would certainly like to know if there are other options. I also tried to use struct
s to do the job, without success (at least considering a struct
with an enum
erator).
Upvotes: 9
Views: 4695
Reputation: 60068
I've been working on a system that very much depend on namespaces (build-type inserted-ones that is), and from my looking at C, you can imperfectly emulate C++
namespaces (on Linux anyway) by collecting the symbols your exports for static linking, prefixing them with objcopy
and then macro-translating your header code (excluding includes) to match the prefixed symbol set.
The problem with this is that macros don't respect scope and so assuming your library exports void foo()
(or symbol foo
in short) -- and you'd prefix that into mylib_foo
, then a macro that translates foo
into mylib_foo
would also indiscriminately translate struct members named foo
even though those should not be translated.
I believe that doing it right would actually require hacking at the compiler (and somebody do it please! :)).
Upvotes: 0
Reputation: 169603
First, you start out by prefixing all exported symbols (including names of preprocessor defines and enum members) with a namespace. For example, you could have a function declaration
void foo_bar_baz(void);
On the C++ side, these need to be wrapped in extern "C" { … }
and then should be registered with the correct namespace. Assuming C++11, in case of functions this should be as simple as
namespace foo {
namespace bar {
constexpr auto baz = foo_bar_baz;
}
}
On the C side, you could define shortened names like
#define baz foo_bar_baz
use compiler-specific attributes to register an alias or add a constant declaration
static void (*const baz)(void) = foo_bar_baz;
This works out just fine because calling a function actually makes use of function pointers (instead of designators).
You could put everything into a single header with some #ifdef
s as appropriate, or you could provide small wrappers like foo/bar.hxx
for C++ and foo/bar-import.h
for shortened C names in addition to foo/bar.h
which contains the actual prefixed declarations and would be included by the other headers.
Upvotes: 2
Reputation: 45664
Don't emulate namespaces in C at all, go the C way:
_Generic
to simulate overloading on argument-types.Your include file should define those C functions in an inner detail-namespace for C++ (does not change the functions actual identity, due to C-linkage), and then you strip prefixes and suffixes from the C functions for C++.
It looks like this:
#ifndef MY_HEADER_GUARD_unique_suffix
#define MY_HEADER_GUARD_unique_suffix
#ifdef __cplusplus
namespace my_module {
namespace detail {
extern "C" {
#endif
// Defines for common structs and functions here
// Also inline functions written in the common intersection of C and C++
#ifdef __cplusplus
}
}
using init = detail::my_module_init;
using close = detail::my_module_close;
}
#endif
#endif
You might also want to add member-functions to some of the C structs for the C++ interface, which might be inline-functions delegating to a shared function.
Upvotes: 7
Reputation: 13370
You can hide all of your exported functions from being exported from the module with static
at the definition level, so that no names are placed in the global space, and instead put them in a struct that is the only thing provided by the module.
E.g. foo.h:
struct ns_t {
int (*a)(int, int);
void (*b)(float, float);
};
extern struct ns_t ns;
foo.c:
#include "foo.h"
static int a(int x, int y) {
...
}
static void b(float x, float y) {
...
}
struct ns_t ns = { .a = a, .b = b };
bar.c:
#include "foo.h"
....
ns.b(4.5, 6.8);
....
Upvotes: 12