先决条件:
我无法获得提升。
我有第三方 C 标头,描述结构的内容,例如
typedef struct
{
int int_value;
char char_value;
bool bool_value;
}
example_struct_t;
我无法对其进行任何更改。
目标:
我需要以自定义文本格式序列化该结构中的数据。
我计划制作按类型的序列化器,例如
SerializerBase, SerializerINT::SerializerBase
,SerializerCHAR::SerializerBase
等。
并在一些模板类中使用它,(伪代码):
Serializer<example_struct_t = T>
{
foreach_field_in<T> is field
{
if(field.type == typeof(int))
{
AddToResult(SerializerINT::Serialize(field.value));
}
}
}
或者一些不太“模板化”的东西(伪代码):
class SerializationSequence<example_struct_t = T>
{
std::vector<SerializerBase> sequence;
SerializationSequence()
{
sequence = GenerateMethodsSequenceForThisStruct<T>(); // Compile time
}
}
C++ 有什么方法可以在编译时解析这个结构并制作一些东西,比如
std::tuple<int,char,bool>
?
C++ 反射提案可以轻松做到这一点。 它还没有用 C++ 编写。
可以通过多种方式获取 C++ 解析树。 这是相当极端的,涉及黑客攻击或使用自定义编译器。
最后,还有一些技巧涉及检测支架结构的空气性并使用
[]
进行拆包。 这可能是最实用的了。
基本思想是您可以在即时上下文中测试
T{a0, a1, a2}
是否合法。 您创建一个具有“通用”template<class T>operator T()
的类型,并找到可以使用 {}
s 创建类型实例的最大数量。
然后你通过
解压auto&& [b0, b1, b2] = t;
return std::make_tuple(b0,b1,b2);
并从结果元组中提取类型,并为每个计数提供自定义(“手写”)版本。
它需要一堆代码生成,具有最大的处理能力,它只能很好地处理 POD 类型(带有一些扩展)。 但该技术是当前 C++ 中最接近的技术。
这里有人使用这种方法。 我不知道他们是否做得好,他们使用的许可证等等;这只是其他人使用此技术的一个例子。
OP 写道,使用 boost 是不可能的,但随后指出包含单个标头是可能的。 magic_get 又名 boost.pfr 是一个 1 头库,并且应该完全按照 OP 的要求:将一个简单的结构转换为一个元组。请参阅 github.com/apolukhin/magic_get
根据 Yakk 的回答,这是一个独立的解决方案;它的“独创性”在于可以生成代码(通过 python)来测试最多 N 个属性。
#include <type_traits>
#include <tuple>
#include <cstdint>
////////////////////////////////////////////////////////////////////////////////////////////////////
template< class T, class ...Args >
inline constexpr bool is_brace_constructible_v = requires { T {std::declval<Args>()...}; };
struct any_type { template<class T> constexpr operator T(); };
template<class T, typename FUNCTION>
constexpr auto fields_call (T&& object, FUNCTION f)
{
using type = std::decay_t<T>;
using at = any_type;
/************************* PYTHON for code generation *************************
N = 3;
for i in reversed(range(1,N+1)):
s = [];
l = ",".join(["p{:d}".format(n) for n in range(1,i+1)]) ;
s.append (("else " if i<N else "") + "if constexpr(is_brace_constructible_v<type,{:s}>)".format (",".join(['at']*i)));
s.append ("{");
s.append (" auto&& [{:s}] = object;". format(l) );
s.append (" return f({:s});".format(l));
s.append ("}");
print ("\n".join(s));
*/
// BEGIN GENERATED CODE
if constexpr(is_brace_constructible_v<type,at,at,at>)
{
auto&& [p1,p2,p3] = object;
return f(p1,p2,p3);
}
else if constexpr(is_brace_constructible_v<type,at,at>)
{
auto&& [p1,p2] = object;
return f(p1,p2);
}
else if constexpr(is_brace_constructible_v<type,at>)
{
auto&& [p1] = object;
return f(p1);
} // END GENERATED CODE
}
template<typename T>
struct to_tuple
{
using type = decltype (fields_call (T{}, [] (auto&&...args)
{
return std::make_tuple (std::forward<decltype(args)>(args)...);
}));
};
template<typename T>
using to_tuple_t = typename to_tuple<T>::type;
////////////////////////////////////////////////////////////////////////////////////////////////////
struct foo
{
uint8_t a;
uint16_t b;
uint32_t c;
};
static_assert (std::tuple_size_v <to_tuple_t<foo>> == 3);
static_assert (std::is_same_v <decltype(foo::a), std::tuple_element_t<0,to_tuple_t<foo>>> == true);
static_assert (std::is_same_v <decltype(foo::b), std::tuple_element_t<1,to_tuple_t<foo>>> == true);
static_assert (std::is_same_v <decltype(foo::c), std::tuple_element_t<2,to_tuple_t<foo>>> == true);
请注意,还可以使用
fields_call
来迭代给定结构的所有属性:
template<class T, typename FUNCTION>
auto fields_iterate (T&& object, FUNCTION fct) noexcept
{
fields_call (object, [&] (auto&&...args) { ( fct(std::forward<decltype(args)>(args)), ...); });
}