Home > Enterprise >  Using a enum class from a c header in a c header
Using a enum class from a c header in a c header

Time:05-05

I am writing a c wrapper around a c library. In the c there are enum classes used as types for function arguments. How do I use theme correctly in the c header.

One ugly way would be to use int's in the c function and cast theme in the wrapper function to the enum type. But this gives the user of the c function no clue about the valid values, and it is really hard to check if the value is valid.

cpp header

namespace GPIO
{
    enum class Directions
    {
        UNKNOWN,
        OUT,
        IN,
        HARD_PWM
    };

    void setup(int channel, Directions direction, int initial = -1);
}

c wrapper header

    int setup(int channel, int direction, int initial);

c wrapper code

   int setup(int channel, int direction, int initial)
   {
        GPIO::setup(channel, static_cast<GPIO::Directions>(direction), initial);
        return 0;
    }

What would be a good way to give the user of the c functions the benefits of the enum classes in the c library. Because it is not my library, I would like to not change too much of the code in the library.

There would be the option to extract the enum classes to a different file and include it in the original header. But I don't know how to define it correctly, so I don't have to change the naming in the cpp library and still can use it in the c header.

CodePudding user response:

You can not do it. It is impossible to use C features from C code. You are creating C wrapper for C function, why can not you create also C wrapper for enum? The only question is how to be sure that both enums have the same values. You can check it compile time after the small code change:

cpp header:

namespace GPIO
{    
    enum class Directions
    {
        UNKNOWN,
        OUT,
        IN,
        HARD_PWM,
        SIZE
    };
}

c wrapper header:

enum GPIO_Directions
{
    GPIO_Directions_UNKNOWN,
    GPIO_Directions_OUT,
    GPIO_Directions_IN,
    GPIO_Directions_HARD_PWM,
    GPIO_Directions_SIZE
};

c wrapper code:

   int setup(int channel, GPIO_Direction direction, int initial)
   {
       static_assert(GPIO::Directions::SIZE == GPIO_Directions_SIZE, 
                       "c wrapper enum  must be equal to c   enum");             
       GPIO::setup(channel, static_cast<GPIO::Directions>(direction), initial);
       return 0;
    }

CodePudding user response:

You cannot use the C code in C because it hasn't been written in common subset of the languages.

You can define a corresponding enum in the C wrapper like this for example:

// C
enum Wrapper_Directions
{
    Wrapper_Directions_UNKNOWN,
    Wrapper_Directions_OUT,
    Wrapper_Directions_IN,
    Wrapper_Directions_HARD_PWM,
};

int wrapper_setup(int channel, enum Wrapper_Directions direction, int initial);

CodePudding user response:

Assuming you are in control of the C headers, too, then you can let the pre-processor generate the enum definitions; you need a set of macros for:

genEnumDefine.h:

// DON'T want include guards!
// otherwise including several headers defining enums that way would fail!

#ifdef __cplusplus

#define ENUM_DEFINITION(NAMESPACE, NAME, CONTENT) \
namespace NAMESPACE                               \
{                                                 \
enum class NAME                                   \
{                                                 \
    CONTENT(NAMESPACE, NAME)                      \
};                                                \
}
#define ENUM_ENTRY(N, E, V) V

#else

#define ENUM_DEFINITION(NAMESPACE, NAME, CONTENT) \
enum NAMESPACE##_##NAME                           \
{                                                 \
    CONTENT(NAMESPACE, NAME)                      \
};
#define ENUM_ENTRY(N, E, V) ENUM_ENTRY_(N, E, V)
#define ENUM_ENTRY_(N, E, V) N##_##E##_##V

#endif

genEnumUndef.h:

#undef ENUM_DEFINITION
#undef ENUM_ENTRY
#ifndef __cplusplus
#undef ENUM_ENTRY_
#endif

Now you can define an enum simply as:

#include <genEnumDefine.h>

#define ENUM_N_E(NAMESPACE, NAME)        \
    ENUM_ENTRY(NAMESPACE, NAME, E1 = 1), \
    ENUM_ENTRY(NAMESPACE, NAME, E2),     \
    ENUM_ENTRY(NAMESPACE, NAME, E3)

ENUM_DEFINITION(N, E, ENUM_E)

#include <genEnumUndef.h>

You could even define both enums in one single header! You would change the check for __cplusplus for a custom definition and could then do the following:

#define ENUM_N_E(NAMESPACE, NAME)        \
    ENUM_ENTRY(NAMESPACE, NAME, E1 = 1), \
    ENUM_ENTRY(NAMESPACE, NAME, E2),     \
    ENUM_ENTRY(NAMESPACE, NAME, E3)

#ifdef __cplusplus

#define GEN_ENUM_CPP 1
#include <genEnumDefine.h>

ENUM_DEFINITION(N, E, ENUM_E)

#include <genEnumUndef.h>
#undef GEN_ENUM_CPP

#endif

#include <genEnumDefine.h>

ENUM_DEFINITION(N, E, ENUM_E)

#include <genEnumUndef.h>

Just for illustration...

Life demo (implicit C/C check variant).

  • Related