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