c++ - 有条件地初始化结构字段 - 仅当它存在于该结构中时

标签 c++ api unit-testing reflection initialization

为非常简单的 API 开发单元测试框架之类的东西:

extern "C" void execute_command(int cmd, void *params);

它根据 cmd 参数将 params 转换为适当的结构。我无法更改该接口(interface),也无法修改指定命令和不同参数结构(均为 POD)的 header 。

我确实可以访问类似这样的数组:

{ 0 /*cmd number*/, "PARAM_STRUCT_FOR_CMD_0", sizeof(PARAM_STRUCT_FOR_CMD_0) }

这些参数结构有一些共同的属性。例如,它们中的许多都有一个类似 void *pObject; 的字段,尽管它并不总是相同的偏移量。为了说明,假设有三种结构:

struct {
    void *pObject;
    int someData;
} PARAM_STRUCT_FOR_CMD_0;
struct {
    float someFloatData;
    void *pObject;
} PARAM_STRUCT_FOR_CMD_1;
struct {
    float someFloatData;
    void *pAnotherObject;
} PARAM_STRUCT_FOR_CMD_2;

这两个pObject字段代表的是同一个东西,而pAnotherObject是不相关的。

现在,谈谈我真正想要的:我想根据 cmdvoid* 转换为某个结构,并设置它的 pObject 字段,如果它存在于该结构中。理想情况下,我能够执行以下操作:

void *pGlobalObject;
void execcmd(int cmd)
{
    static uint8_t params[MAX_SIZE_OF_PARAM_STRUCT];

    memset(params, 0, MAX_SIZE_OF_PARAM_STRUCT);
    INIT_STRUCT_IF_POSSIBLE(cmd, (void*)params);
    execute_command(cmd, params);
}

INIT_STRUCT_IF_POSSIBLE 可能是这样的:

#define INIT_STRUCT_IF_POSSIBLE(cmd, str) \
do {  \
    switch (cmd)  \
    {  \
        case 0: static_cast<PARAM_STRUCT_FOR_CMD_0*>(str)->pObject = pGlobalObject; break;  \
        case 1: static_cast<PARAM_STRUCT_FOR_CMD_1*>(str)->pObject = pGlobalObject; break;  \
        case 2: /* nothing, no pObject field */ break;  \
    }  \
} while (0)

除了它不是真正可扩展的。我有大约 1000 个可能的命令,假设我想设置 5 个字段(没有结构包含全部 5 个),并且可以添加新命令,所以我想避免手动更改它。

显而易见的解决方案是一个额外的构建步骤来解析所有结构,并创建它们的初始化器。但是,由于项目的结构,添加这个额外的构建步骤很痛苦,所以我希望有一个纯 C++ 解决方案。

如果有一种方法可以使用 C 预处理器生成初始值设定项,我完全赞成。如果它能以某种方式使用模板来完成,那也一样好。如果有帮助,我有可用的 boost 和 C++11。

解决这个问题的一件事是指定初始化器,例如 STR x = {.pObject = pGlobalObject; };。不幸的是,当该字段不可用时,它们会导致错误。有什么办法可以忽略不存在的字段吗? (是的,我知道它们只是 C,不是 C++,但如果需要我可以切换到 C)

最佳答案

欢迎来到 SFINAE 的世界

template<typename T>
typename std::enable_if<
   std::is_same<decltype(T::pObject), void*>::value
>::type setPobject(T *t) {
   t->pObject = pGlobalObject;
}

void setPobject(void *t) { }

template<typename T>
typename std::enable_if<
   std::is_same<decltype(T::someFloatData), float>::value
>::type setSomeFloatData(T *t) {
   t->someFloatData = someGlobalFloat;
}

void setSomeFloatData(void *t) { }

// ...

只需为所有具有正确类型的对象调用它们,它们就会自行判断它们是否适用。您还可以自动化类型转换

template<typename D>
struct Call {
    static void call(void *t) {
       setPobject(static_cast<D*>(t));
       setSomeFloatData(static_cast<D*>(t));
    }
};

// desginated initializers here for convenience (non-C++)
void (* const table[])(void*) = {
   [0] = Call<PARAM_STRUCT_FOR_CMD_0>::call,
   [1] = Call<PARAM_STRUCT_FOR_CMD_1>::call
   // ...
};

关于c++ - 有条件地初始化结构字段 - 仅当它存在于该结构中时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33108503/

相关文章:

c++ - 为什么 std::vector 的元素不需要默认构造函数?

linux - 'Kubectl' 在使用 docker 安装时抛出错误 'failed to negotiate an api version'

java - 如何以及何时使用 Mockito Annotations 和 jUnit 进行清理

Spring 启动测试 - 找不到测试属性

C++ 变量在 While 循环中不更新

c++ - 此代码如何显示此输出?

c++ - 在 OpenGL : improving frames-per-second 中实例化数百万个对象

api - Django allauth 使用谷歌账户登录

javascript - 如何在 React js 中为来自 api 的动态表单字段设置一个通用的 onchange?

sql - PL/SQL 包自动化测试