在 c 标头中使用来自 c++ 标头的枚举类

Using a enum class from a c++ header in a c header

提问人:ludw 提问时间:5/5/2022 最后编辑:ludw 更新时间:5/5/2022 访问量:634

问:

我正在围绕 c++ 库编写一个 c 包装器。 在 c++ 中,有枚举类用作函数参数的类型。 如何在 c 标头中正确使用主题。

一种丑陋的方法是在 c 函数中使用 int,并将包装函数中的主题转换为枚举类型。但是这让 c 函数的用户对有效值一无所知,并且很难检查该值是否有效。

CPP 接头

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

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

C 包装器标头

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

C 包装器代码

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

让 c 函数的用户享受 c++ 库中枚举类的好处的好方法是什么?因为它不是我的库,所以我不想更改库中的太多代码。

可以选择将枚举类提取到不同的文件并将其包含在原始标头中。但是我不知道如何正确定义它,所以我不必更改 cpp 库中的命名,仍然可以在 c 标头中使用它。

C++ C 举 枚 举类 extern-c

评论

1赞 273K 5/5/2022
为什么不用代替?enum Directionsint
0赞 wohlstad 5/5/2022
@273K我认为 OP 无法在 c 标头中 #include cpp 标头。
0赞 5/5/2022
由于 C 中没有类,因此无法从枚举类的功能中受益。使用普通枚举。
1赞 Gerhardh 5/5/2022
int initial = -1C 中没有默认值。
2赞 Lundin 5/5/2022
围绕简单的GPIO编写英国媒体报道软件包装器从来都不是一个好主意。除非此类提供高级GPIO功能,如中断处理、位敲击、端口路由等,否则只需摆脱它并直接写入寄存器即可。更快、更易读、更便携、更易于维护。C++ 程序通常带有混淆代码或提供额外无意义抽象层的疾病。

答:

0赞 eerorika 5/5/2022 #1

不能在 C 中使用 C++ 代码,因为它不是用语言的常见子集编写的。

您可以在 C 包装器中定义相应的枚举,如下所示:

// 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);

评论

0赞 ludw 5/5/2022
我想到了这一点,问题是,当你在“main”库的 cpp 枚举类中更改某些内容时,包装器中的代码也必须更改。从维护的角度来看,这不是很好。是否有强制维护者更新两者的选项?
1赞 eerorika 5/5/2022
@ludw 您可以通过元编程将其自动化。
0赞 ScottMichaud 9/12/2023
一种策略是(尽可能多地)在难以记住更新的地方static_assert一些鸭子测试。
4赞 Sandro 5/5/2022 #2

你做不到。无法从 C 代码中使用 C++ 功能。您正在为 C++ 函数创建 C 包装器,为什么不能为枚举创建 C 包装器?唯一的问题是如何确保两个枚举具有相同的值。你可以在小代码改动后检查它的编译时间:

CPP 标头:

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

C 包装器标头:

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

C 包装代码:

   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;
    }
2赞 Aconcagua 5/5/2022 #3

假设您也控制了 C++ 标头,那么您可以让预处理器生成枚举定义;您需要一组宏来执行以下操作:

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

现在,您可以简单地将枚举定义为:

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

您甚至可以在一个标头中定义两个枚举!您可以更改自定义定义的检查,然后可以执行以下操作:__cplusplus

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

仅供参考...

生活演示(隐式 C/C++ 检查变体)。