Momergil
Momergil

Reputation: 2281

How can one emulate the C++ namespace feature in a C code?

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++ namespaces - 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 structs to do the job, without success (at least considering a struct with an enumerator).

Upvotes: 9

Views: 4695

Answers (4)

Petr Skocik
Petr Skocik

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

Christoph
Christoph

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 #ifdefs 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

Deduplicator
Deduplicator

Reputation: 45664

Don't emulate namespaces in C at all, go the C way:

  • Use prefixes instead of namespaces.
  • Use suffixes instead of overloading.
    • Optionally use a macro with _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

Alex Celeste
Alex Celeste

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

Related Questions