基于模板类型的枚举值

时间:2019-08-16 10:40:07

标签: c++ templates enums

tl; dr:有没有一种方法可以从typename派生枚举值?

详细信息: 我正在尝试对函数进行模板化以在每个结构数据之前发送标头。标头存储在枚举类中,例如:

enum class TypeHeader {
  Test1 = 4;
  Test2 = 16;
  Test3 = 50;
};

我有许多带有字段和类型的结构。 示例结构:

struct Test1 {
  uint32_t field1;
  uint32_t field2;
}

struct Test2 {
  uint8_t field1;
  uint8_t field2;
}

struct Test3 {
  uint8_t field1;
  uint16_t field2;
}

结构体的名称与枚举字段相同(不一定,我认为这样可能会使事情变得更容易)

将数据发送到服务器的功能:

template<typename T>
void send(const uint8_t* const s) {
  uint8_t data[2 + sizeof(T)];

  // data[1] = TypeHeader::???? << 8
  // data[0] = TypeHeader::????
  // i want data[0] and data[1] to have the struct id
  // as a uint16 which is in the enum TypeHeader

  memcpy(data + 2, s, sizeof(T));

  // then data is ready and I can send it...
}

我知道我可以在模板或函数中添加一个附加参数并传递TypeHeader值,但是我想知道编译器是否可以将类型名链接到枚举标记,因此我可以做这样的事情:

Test1 test1; 
send(test1);

2 个答案:

答案 0 :(得分:4)

您应该像这样将值作为静态元素添加到您的结构定义中:

enum class TypeHeader: uint16_t { // make it really a 16bit as you later access it as such!
  Test1 = 4,
  Test2 = 16,
  Test3 = 50,
};



struct Test1 {
  uint32_t field1;
  uint32_t field2;
  static constexpr TypeHeader header = TypeHeader::Test1; // not part of object but of the type!
};

struct Test2 {
  uint8_t field1;
  uint8_t field2;
  static constexpr TypeHeader header = TypeHeader::Test2;
};

struct Test3 {
  uint8_t field1;
  uint16_t field2;
  static constexpr TypeHeader header = TypeHeader::Test3;
};


template<typename T>
void send(const T& obj)
{
  uint8_t data[2 + sizeof(T)];

   // converting to the given type, even if T changes    
   data[1] = (std::underlying_type_t<decltype(obj.header)>)T::header >> 8; // you wrongly used <<!
   data[0] = (std::underlying_type_t<decltype(obj.header)>)T::header & 0xff;

  memcpy(data + 2, &obj, sizeof(T));
}


int main()
{
    Test1 t1{ 1,2 };
    send( t1 );
}

编辑:添加演员表的描述:

obj.header是一个不能直接用作数值的枚举。但是您想将其作为具有8位低和高值的2字节缓冲区写入到sendbuffer中。因此,我们必须将其转换为整数值。枚举值定义为uint16_t值。好的,但是我们不想复制该定义,因为也许我们以后想更改定义。因此,准确使用我们使用的变量类型是一个很好的方法。或者,如果我们想更通用地使用send函数,我们还可以使用不同的枚举值,这些值都可以转换为我们的缓冲区数字表示形式。也许我们在send函数中还有其他内容,使其能够使用32位值或您想要的任何值。我添加了这个强制转换想法,为您提供了一个示例,说明如何准备模板化的函数以使其更加通用。是的,您也可以直接转换为uint16_t。但是,如果您使用编译器已经掌握的知识,那么使程序更易于维护始终是一个好主意。如果您写两次(在枚举定义中重复一次,而在转换时再次重复)您的类型,那么如果您更改了定义,则有机会以后忘记一次。

我们在这里:

  • obj.header是变量,即使它是const
  • decltype(obj.header)为我们提供了枚举类型
  • std::underlying_type_t现在为我们提供了代表枚举的整数类型。我们将其定义为uint16_t!而我们通常使用这种类型。

下一个补充:OP要求提供Followig代码,并以我的想法发表评论:

template <typename T> void send(const T& obj)
{ 
    auto header = (std::underlying_type_t<decltype(obj.header)>)T::header;

    // if your header now vary in the size, you must add an additional header
    // size information! If not, the receiver did not know if data is from 
    // header or from the following data field
    uint8_t data[sizeof(header) + sizeof(T)];

    // now you have the order of bytes in the header
    // in the same order as the machine uses ( endianess )
    // That is ok as long you use the same system architecture
    // to read back the data but it is not portable
    memcpy(data, &header, sizeof(T));

    // here you definitely write the data in the architecture dependent
    // order and also with potential padding bytes... 
    // As long as you know what you do, everything is fine.
    // But if you change from 32 bit to 64 bit or use pragma(pack)
    // you will change your serialized format!
    memcpy(data + sizeof(header), &obj, sizeof(T));
}

答案 1 :(得分:1)

您不能使用类型(来自模板)作为枚举名称。

还有其他选择,例如模板变量:

template <typename T> static const uint16_t TypeHeader;

template <> const uint16_t TypeHeader<Type1> = 4;
template <> const uint16_t TypeHeader<Type2> = 16;
template <> const uint16_t TypeHeader<Type3> = 50;

然后像这样使用它:

template<typename T>
void send(const uint8_t* const s) {
    uint8_t data[2 + sizeof(T)];

    data[0] = TypeHeader<T> & 0xFF;
    data[1] = (TypeHeader<T> >> 8) & 0xFF;

    memcpy(data + 2, s, sizeof (T));

    // then data is ready and I can send it...
}