2

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.

ludw
  • 113
  • 8
  • 1
    Why not use `enum Directions` instead of `int`? – 273K May 05 '22 at 07:25
  • @273K I think the OP cannot #include the cpp header in the c header. – wohlstad May 05 '22 at 07:29
  • As there are no classes in C, you can't benefit from the features of the enum class. Use ordinary enums. –  May 05 '22 at 07:32
  • 1
    `int initial = -1` There are no default values in C. – Gerhardh May 05 '22 at 07:33
  • @273K The problem is, that I have multiple enum classes with the overlapping names in it, so plain enum does not work. – ludw May 05 '22 at 07:35
  • @Gerhardh Was a copy past error, I corrected it. – ludw May 05 '22 at 07:37
  • @wohlstad 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. – ludw May 05 '22 at 07:39
  • Well, if there's no way to switch from `enum class` to classic `enum` within CPP then there's no other way as to re-write the enums in C, if you insist on providing them there as well. You might possibly automate that by a script, though. To avoid name collisions the script could prepend the namespace names to the enum names and values (`enum GPIO_Directions { GPIO_UNKNOWN, ... };`). – Aconcagua May 05 '22 at 07:47
  • 2
    Writing bloatware wrappers around simple GPIO is never a good idea. Unless this class provides _advanced_ GPIO features like interrupt handling, bit-banging, port routing etc, then simply get rid of it and write to the registers directly. Faster, more readable, more portable, more maintainable. C++ programs often come with the disease of obfuscating code or providing extra pointless abstraction layers just for the heck of it. – Lundin May 05 '22 at 07:51
  • Can you re-write the C++ headers or are these 3rd party??? – Aconcagua May 05 '22 at 07:52
  • @Aconcagua, theoretically I could rewrite the headers. – ludw May 05 '22 at 07:56
  • @Aconcagua, I'm writing code for the NVIDIA Jetson with a Linux operating system. I don't want to implement everything new, and It is not as easy as on a microcontroller. But I have some other library that is written in c that needs gpio functionality. – ludw May 05 '22 at 07:58
  • The point is: You cannot use C++ features from C, so you need to duplicate these in C in some compatible way. One way might be *generating* the C headers from the C++ ones via a script as proposed already; another one might be letting the pre-processor generate the appropriate headers for both C and C++, you'd write some appropriate macros for, most likely X macros, to generate the enum definitions for the respective languages. That requires being able to modify the C++ headers, though. – Aconcagua May 05 '22 at 08:05
  • @Aconcagua I'm able to change the header a bit. I forked the library and wanted to add a wrapper with the idea to include it in the original library. So as long the cpp code has not to be changed, It is ok. The library I use: [library](https://github.com/ma-ludw/JetsonGPIO/tree/c_wrapper) – ludw May 05 '22 at 08:11

3 Answers3

4

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;
    }
Sandro
  • 2,707
  • 2
  • 20
  • 30
2

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).

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
0

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);
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • I thought about that, the problem is, when ever you change something in the cpp enum class in the "main" library, the code in the wrapper has to be changed too. This is not very good from a maintenance point. Is there an option to enforce the maintainer to update both? – ludw May 05 '22 at 07:54
  • 1
    @ludw You can automate it with meta-programming. – eerorika May 05 '22 at 07:55