首页 > 解决方案 > 使用 C++17 中声明的联合初始化 C 结构

问题描述

这个问题专门针对C++17中的选项。假设 C 库中的以下声明(我无法更改它们):

typedef enum {
    TYPEA = 0,
    TYPEB = 2,
    TYPEC = 4
} SPECIFIC_TYPE_t;

typedef struct {
    uint16_t Method : 1;
    uint16_t Access : 2;
    uint16_t VendorSpecific : 1;
    uint16_t Direction : 1;
    uint16_t Persistent : 1;
    uint16_t Internal : 4;
    uint16_t Reserved : 6;           
} PROPERTY_t;

typedef RESULT_t (*CB_DataPointRead_t) (void *Service, uint8_t Pinpoint, bool VendorSpecific,
                                        uint16_t GroupID, uint16_t ElementID, void *Data,
                                        uint8_t *DataLengthInOut);
typedef RESULT_t (*CB_DataPointWrite_t) (void *service, uint8_t Pinpoint, bool VendorSpecific,
                                         uint16_t GroupID, uint16_t ElementID, void *Data,
                                         uint8_t DataLength);

typedef struct {
    uint16_t GroupID;
    uint16_t ElementID;
    uint8_t Pinpoint;
    SPECIFIC_TYPE_t Type;
    uint8_t Size;
    PROPERTY_t Property;
    union {
        struct {
            CB_DataPointRead_t Read;
            CB_DataPointWrite_t Write;
        } Callback;
        struct {
            void *Data;
        } DirectAccess;
    } AccessType;
} DataPoint_t;

纯 C 中的状态

初始化最后一个元素指示符初始化器在 C 中运行良好:

uint16t dataPointValue = 0;
const DataPoint_t firstDatapointConfig = {
    /*...*/,
    .DirectAccess = { (void*)&dataPointValue; }
};

C++ 中的指示符初始值设定项

它们出现在 C++20 中,在许多方面与 C 不兼容。

问题

firstDatapointConfig想像 C++17 中的 const 一样初始化变量。到目前为止,除了在 C 中编写函数(编译为 C 代码)并在使用前将初始化结构返回给变量之外,我没有看到其他方法。我尝试了各种方法,包括处理指示符初始化程序的 gnuc++17,但它告诉我:

error: 'const DataPoint_t' has no non-static data member named 'DirectAccess'

如果没有启用 C++20,MSVC 根本不会消化这种初始化方法。

解决初始化程序之外的最后一个元素,也不起作用:

datapoint.DirectAccess = { &value };

导致以下错误:

error: 'struct DataPoint_t' has no member named 'DirectAccess'

评论

在通过 bindgen 处理它们之后,在 Rust 中使用这些结构要容易得多:-)

问题

有没有办法用填充正确值的元素初始化DataPoint_tC++17 中的类型变量?DirectAccess

标签: c++c++17unions

解决方案


有没有办法用填充了正确值的 DirectAccess 元素初始化 C++17 中的 DataPoint_t 类型的变量?

是的,尽管我的做法有点麻烦。可能有更简单的方法:

// Make a usable type of that anonymous entity:
using AccessType_t = decltype(DataPoint_t::AccessType);

// Use the new type and prepare what you need:
AccessType_t at;
at.DirectAccess.Data = nullptr;

// initialize your DataPoint_t
const DataPoint_t firstDatapointConfig{1,2,3,TYPEA,4, PROPERTY_t{}, at};

如果你经常这样做,你可以创建一个辅助函数:

using AccessType_t = decltype(DataPoint_t::AccessType);
using Callback_t = decltype(AccessType_t::Callback);    
using DirectAccess_t = decltype(AccessType_t::DirectAccess);

template<class U>
constexpr auto init_AccessType(U u) {
    AccessType_t at;

    if constexpr (std::is_same_v<U,Callback_t>) {
        at.Callback = u;
    } else if constexpr (std::is_same_v<U,DirectAccess_t>) {
        at.DirectAccess = u;
    } else {
        // uninitialized
    }
    return at;
}

const DataPoint_t firstDatapointConfig{1,2,3,TYPEA,4, PROPERTY_t{},
                                       init_AccessType(DirectAccess_t{nullptr})};

推荐阅读