C++ C++;元编程:基于类型/值输入生成字节序列?
所以这可能是一个“显然很容易”或“显然不可能”的问题 但是想象一下一个简单的缓冲协议,其中数据的前缀是一个表示类型的字节。因此,对于假布尔值,您将使用C++ C++;元编程:基于类型/值输入生成字节序列?,c++,serialization,deserialization,metaprogramming,c++17,C++,Serialization,Deserialization,Metaprogramming,C++17,所以这可能是一个“显然很容易”或“显然不可能”的问题 但是想象一下一个简单的缓冲协议,其中数据的前缀是一个表示类型的字节。因此,对于假布尔值,您将使用04 00,对于值为1的uint32\u t,您将使用02 00 01 是否可以在编译时序列化数据,以便序列化(false、true、uint32_t(3))返回一个序列,如04 00 04 01 02 00 00 03 如果是这样的话,是否可以创建这样的准备语句,即在运行时可以填充的序列中只有零?甚至可以做一些基本的单元测试,比如static\u
04 00
,对于值为1的uint32\u t,您将使用02 00 01
是否可以在编译时序列化数据,以便序列化(false、true、uint32_t(3))
返回一个序列,如04 00 04 01 02 00 00 03
如果是这样的话,是否可以创建这样的准备语句,即在运行时可以填充的序列中只有零?甚至可以做一些基本的单元测试,比如static\u断言(反序列化(“\x04\x01\x04\x00”)[0]==true,“Error”)代码>
//例如类似这样的东西。
模板constexpr uint8\u t前缀()
{
如果constexpr(std::is_same_v)返回0x04;
如果constexpr(std::is_same_v)返回0x02;
返回0x00;
};
模板字符串写入(T值)
{
ctString Temp{};
Temp+=写入(前缀());
温度+=/*某物大小(值)*/;
返回温度;
};
模板ctString序列化(标准::元组输入)
{
ctString Temp{};
std::apply([&](自动参数){Temp+=Write(参数);},输入);
返回温度;
};
constexpr auto Message=Serialize(false、true、uint32_t(3));
类似这样的内容:
template <typename... Args>
constexpr auto serialize(Args... args) {
// better not pass in std::string or something...
static_assert((std::is_trivially_copyable_v<Args> && ...));
// each size better fit in a byte
static_assert((sizeof(Args) < 256 && ...));
std::array<char,
(sizeof...(Args) + // one byte for each arg
(sizeof(Args) + ...) // sum of the sizes of the args
)> buffer;
char* p = buffer.data();
auto fill = [](char* p, auto arg) { /* ... */ };
(p = fill(p, args), ...);
return buffer;
}
为了好玩,我提出了一个只在编译时工作的模板元编程版本,并将序列化存储在std::integer_序列
类型中(显然,可以使用unsigned char
或std::uint8_t
或其他类型的intead)
因此,序列化是通过传递希望序列化为模板参数的值来获得类型来完成的(这只从C++17开始工作,因为使用auto
模板值类型)
下面是一个完整的编译(C++17)示例
#包括
#包括
#包括
样板
struct IntChs:public IntChs>8)、char(V&0xff)、Cs…>
{ };
样板
结构集成
{using type=std::integer_序列;};
样板
使用IntChs\u t=typename IntChs::type;
样板
结构匹配;
样板
结构连接
{using type=std::integer_序列;};
样板
结构SerVal;
//标准::uint32_t案例
样板
结构SerVal
:公开辩论
{ };
//布尔案
样板
结构SerVal
:公开辩论
{ };
// ******************************** //
//要添加的其他序列化案例//
// ******************************** //
样板
结构ConcatSer:public SerVal
{ };
样板
使用ConcatSer_t=typename ConcatSer::type;
样板
结构序列化;
样板
结构序列化
:公开序列化
{ };
样板
结构序列化
{使用type=T;};
样板
使用Serialize\u t=typename Serialize::type;
样板
constexpr T Val()
{
T ret{};
((ret C++11、C++14或C++17?如果可以使用C++17,我认为解决方案简单得多。@max66:OP使用如果constexpr
,C++17似乎很好。您在没有meta-prog的情况下成功地编写了它吗?哪一部分没有转换编译时?因此,请尝试使用std::string
,而不是std::array
(编译时字符串的一种):您可以根据类型在编译时计算N
。类似于constepr std::size\u t total\u size=(computeSize()+…);
,在您的情况下,computeSize()
似乎是(1+sizeof(Args))
。
template <typename... Args>
constexpr auto serialize(Args... args) {
// better not pass in std::string or something...
static_assert((std::is_trivially_copyable_v<Args> && ...));
// each size better fit in a byte
static_assert((sizeof(Args) < 256 && ...));
std::array<char,
(sizeof...(Args) + // one byte for each arg
(sizeof(Args) + ...) // sum of the sizes of the args
)> buffer;
char* p = buffer.data();
auto fill = [](char* p, auto arg) { /* ... */ };
(p = fill(p, args), ...);
return buffer;
}
auto fill = [](char* p, auto arg) {
*p++ = sizeof(arg);
for (int i = 0; i < sizeof(arg); ++i) {
*p++ = arg & 0xff;
arg >>= 8;
}
return p;
};
using t1 = Serialize_t<false, true, std::uint32_t{3u}>;
// d, in this case, is a std::tuple<bool, bool, std::uint32_t>
constexpr auto d { Deserialize_v<t1> };
static_assert( std::get<0>(d) == false );
static_assert( std::get<1>(d) == true );
static_assert( std::get<2>(d) == std::uint32_t{3u} );
#include <memory>
#include <iostream>
#include <functional>
template <int I, auto V, char ... Cs>
struct IntChs : public IntChs<I-1, (V >> 8), char(V & 0xff), Cs...>
{ };
template <auto V, char ... Cs>
struct IntChs<0, V, Cs...>
{ using type = std::integer_sequence<char, Cs...>; };
template <int I, auto V>
using IntChs_t = typename IntChs<I, V>::type;
template <typename, char ...>
struct ConcatChs;
template <char ... Cs0, char ... Cs1>
struct ConcatChs<std::integer_sequence<char, Cs0...>, Cs1...>
{ using type = std::integer_sequence<char, Cs1..., Cs0...>; };
template <typename T, T V, char ... Cs>
struct SerVal;
// std::uint32_t case
template <std::uint32_t V, char ... Cs>
struct SerVal<std::uint32_t, V, Cs...>
: public ConcatChs<IntChs_t<4, V>, Cs..., '\x02'>
{ };
// bool case
template <bool V, char ... Cs>
struct SerVal<bool, V, Cs...>
: public ConcatChs<IntChs_t<1, int(V)>, Cs..., '\x04'>
{ };
// ******************************** //
// other serialization cases to add //
// ******************************** //
template <auto V, char ... Cs>
struct ConcatSer : public SerVal<decltype(V), V, Cs...>
{ };
template <auto V, char ... Cs>
using ConcatSer_t = typename ConcatSer<V, Cs...>::type;
template <typename, auto ...>
struct Serialize;
template <char ... Cs, auto V0, auto ... Vs>
struct Serialize<std::integer_sequence<char, Cs...>, V0, Vs...>
: public Serialize<ConcatSer_t<V0, Cs...>, Vs...>
{ };
template <typename T>
struct Serialize<T>
{ using type = T; };
template <auto ... Vs>
using Serialize_t = typename Serialize<std::integer_sequence<char>, Vs...>::type;
template <typename T, char ... Cs>
constexpr T Val ()
{
T ret{};
((ret <<= 8, ret += T(Cs)), ...);
return ret;
}
template <typename, auto...>
struct Deserialize;
// bool case
template <char C0, char ... Cs, auto ... Vs>
struct Deserialize<std::integer_sequence<char, '\x04', C0, Cs...>, Vs...>
: public Deserialize<std::integer_sequence<char, Cs...>,
Vs..., Val<bool, C0>()>
{ };
// std::uint32_t case
template <char C0, char C1, char C2, char C3, char ... Cs, auto ... Vs>
struct Deserialize<std::integer_sequence<char, '\x02', C0, C1, C2, C3, Cs...>,
Vs...>
: public Deserialize<std::integer_sequence<char, Cs...>,
Vs..., Val<std::uint32_t, C0, C1, C2, C3>()>
{ };
// ********************************** //
// other deserialization cases to add //
// ********************************** //
// final case: the tuple
template <auto ... Vs>
struct Deserialize<std::integer_sequence<char>, Vs...>
{ static constexpr auto value = std::make_tuple(Vs...); };
template <typename T>
constexpr auto Deserialize_v = Deserialize<T>::value;
int main()
{
using t1 = Serialize_t<false, true, std::uint32_t{3u}>;
using t2 = std::integer_sequence<char,
'\x04', '\x00', '\x04', '\x01', '\x02', '\x00', '\x00', '\x00', '\x03'>;
static_assert( std::is_same_v<t1, t2> );
constexpr auto d { Deserialize_v<t1> };
static_assert( std::get<0>(d) == false );
static_assert( std::get<1>(d) == true );
static_assert( std::get<2>(d) == std::uint32_t{3u} );
}