【问题标题】:Enum value based on template type基于模板类型的枚举值
【发布时间】:2019-08-16 10:40:07
【问题描述】:

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 值,但我想知道编译器是否有办法将类型名链接到 enum 标记,所以我可以这样做:

Test1 test1; 
send(test1);

【问题讨论】:

  • 您仍然可以将结构保留为 POD 类型,同时仍然添加可以调用以返回 TypeHeader 值的静态成员函数。不过重载可能会更好(然后它将调用一个新的私有 send 函数,该函数将类型和 TypeHeader 作为参数)。
  • 您应该只为特定类型重载send 函数。

标签: c++ templates enums


【解决方案1】:

您应该将值作为静态元素添加到您的结构定义中,如下所示:

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 是一个枚举,我们不能直接将其用作数值。但是您想将其作为 2 字节缓冲区写入您的发送缓冲区,并具有 8 位低值和高值。因此,我们必须将其转换为整数值。枚举值定义为uint16_t 值。好的,但我们不想复制该定义,因为也许我们想稍后更改定义。因此,准确地使用我们使用的变量的类型是一个很好的 ida。或者,如果我们想更通用地使用我们的发送函数,我们也可以使用不同的枚举值,这些值都可以转换为我们的缓冲区数字表示。也许我们在 send 函数中有更多的东西来使它能够使用 32 位值或任何你想要的值。我添加了这个演员的想法,给你一个例子,如何准备你的模板化函数更通用。是的,你也可以直接投到uint16_t。但是,如果您使用已经从编译器获得的知识,那么让您的程序更易于维护总是一个好主意。如果你写了两次(在枚举定义中写一次,在转换时又写一次)你的类型你有很好的机会在以后忘记一次,如果你改变你的定义。

我们开始吧:

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

下一个补充:OP要求以下代码,用我的想法评论:

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

【讨论】:

  • 这正是我想要的,谢谢。但是请您解释一下演员表(std::underlying_type_t)! (是的,请原谅我的位移错误,这只是一个快速的代码注释错误)
  • 所以你的铸造目标是使函数通用。但是data 标头的大小是硬编码的这一事实无法验证额外的冗长,所以我想出了这个: template void send(const T& obj) { auto header = (std::underlying_type_t)T::header; uint8_t 数据[sizeof(header) + sizeof(T)]; memcpy(数据, &header, sizeof(T)); memcpy(数据 + sizeof(header), &obj, sizeof(T)); }
  • @Neut:再次根据您的要求提供一些 cmets。但是对于下一个问题,您应该提出一个新问题 :-) 您添加了越来越多与开始不再相关的主题。这使得很难从其他用户那里找到信息。因此,请随意添加新问题...也许您已经找到答案了 :-) 顺便说一句:对于代码审查,我们这里也有一块板!
  • 是的,很抱歉,这里是新的 :)
  • @Neut: "new here" 正是我想给出一些提示的原因 :-) 你很高兴!
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多