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);
答案 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...
}