Search code examples
c++cenumsextern

Wrap C++ namespaced enum to C interface


I'm trying to wrap an existing 3rd party C++ library to a C interface, so that it can be used in bindings for another language. I'm having trouble figuring out how to wrap a namespaced enum, as opposed to just redefining it:

// Existing C++ 3rd party library header
namespace foo {
    enum Fruit {
        APPLE = 0,
        ORANGE
    }
}

So then I have my wrapped.{h,cpp} with an extern "C" block, and I just can't figure out how to export the foo::Fruit enum into the C interface

// wrapped.h
#ifdef __cplusplus
extern "C" {
#endif

// I don't want to do this
typedef enum Fruit {
    APPLE = 0,
    ORANGE
} Fruit;

#ifdef __cplusplus
}
#endif
#endif

Is it possible to export (mirror) foo::Fruit from the C++ library into my C wrapper as Fruit?


Solution

  • edit: I just noticed that you wanted to wrap an existing library without modifying it.

    I fear you are about out of luck then. In general there is just no way of extracting just the enum members out of C++ code without the C compiler choking.

    In practice you've got the choice whether to programmatically translate your own set of enumerations into the C++ versions in the interface, try to mirror the C++ exactly and place a bunch of static assertions to double-check, or in theory even filtering them out through scripts.

    There are simply no good options here I'm afraid. For the record I would tend to prefer the first of these bad options.


    Personally I probably would be lazy and just stick to the C version.

    Still, if required and the number of constants is large you can do a bit of macro magic to get a single definition with C-style "namespaces" as required.

    First a single header defining all enum entries through a macro:

    /* Fruit.h */
    FOO_ENUM(APPLE) = 0,
    FOO_ENUM(ORANGE)
    

    Then in the C header:

    /* C interface */
    typedef enum {
    #   define FOO_ENUM(id) FOO_##id
    #   include "Fruit.h"
    #   undef FOO_ENUM
    } Foo_Fruit_t;
    

    And finally in the C++ header:

    // C++ interface
    namespace Foo {
        enum Fruit_t {
    #       define FOO_ENUM(id) id
    #       include "Fruit.h"
    #       undef FOO_ENUM
        };
    }
    

    There are many alternatives of course. For instance if you don't mind polluting the global namespace in C++ then can always define the full enumeration directly in the C interface and copy the individual enum members in the C++ version of the definition.