任何 C++20 alternative/tricks 到 P1061 "Structured Bindings can introduce a Pack"?
Any C++20 alternative/tricks to P1061 "Structured Bindings can introduce a Pack"?
有没有办法使用具有任意数量身份的结构化绑定?
P1061 "Structured Bindings can introduce a Pack" 提供了一个方便的解决方案,但目前还不可用。
我想要实现的是为聚合类型提供类似元组的接口。
(std::get<N>(T)
, std::tuple_element_t<T>
, 等等).
我已经有了一个计算字段的函数,所以我现在正在寻找的是一种 - 甚至是棘手的方法 - 来实现以下内容:
template <std::size_t N>
constexpr auto as_tuple(auto && value) noexcept {
auto & [ /* N identities ...*/ ] = value;
return std::tuple/* of references */{ /* N identities... */ };
}
充满绝望,我尝试了一些使用预处理器的想法(免责声明:不是我的那杯茶),没有可扩展的结果。
// 8 bits [0..255]
#define PP_IDENTITY_BIT_1(...) id_##__VA_ARGS__
#define PP_IDENTITY_BIT_2(...) PP_IDENTITY_BIT_1(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_1(__VA_ARGS__ ## 1)
#define PP_IDENTITY_BIT_3(...) PP_IDENTITY_BIT_2(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_2(__VA_ARGS__ ## 1)
#define PP_IDENTITY_BIT_4(...) PP_IDENTITY_BIT_3(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_3(__VA_ARGS__ ## 1)
#define PP_IDENTITY_BIT_5(...) PP_IDENTITY_BIT_4(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_4(__VA_ARGS__ ## 1)
#define PP_IDENTITY_BIT_6(...) PP_IDENTITY_BIT_5(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_5(__VA_ARGS__ ## 1)
#define PP_IDENTITY_BIT_7(...) PP_IDENTITY_BIT_6(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_6(__VA_ARGS__ ## 1)
#define PP_IDENTITY_BIT_8(...) PP_IDENTITY_BIT_7(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_7(__VA_ARGS__ ## 1)
// key idea : conditionaly expand macros, based on bitwise check,
// like `(N & 1) != 0` -> `PP_IDENTITY_BIT_1`
不幸的是,从 C++20 开始,结构化绑定不支持参数包。
你可以通过为每个不同数量的参数提供一个实现来解决这个问题(就像你的例子一样)
1。使用 if constexpr
您可以通过使用 if constexpr
稍微简化它 - 如果条件不为真,则整个语句将被丢弃(并且对于未实例化的模板化实体,即它不必编译给定的模板类型)。
auto
return 类型仅从 non-discarded return 语句推断出 return 类型,因此您可以使用 if constexpr
更改 return函数类型。
这包括:
If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity, if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.
9.2.9.6.1 (3) Placeholder type specifiers
If the declared return type of the function contains a placeholder type, the return type of the function is deduced from non-discarded return statements, if any, in the body of the function.
这允许您像这样编写 as_tuple
函数:
示例:godbolt
#include <tuple>
#include <concepts>
template<class T>
concept aggregate = std::is_aggregate_v<T>;
struct any_type {
template<class T>
operator T() {}
};
// Count the number of members in an aggregate by (ab-)using
// aggregate initialization
template<aggregate T>
consteval std::size_t count_members(auto ...members) {
if constexpr (!requires { T{ members... }; })
return sizeof...(members) - 1;
else
return count_members<T>(members..., any_type{});
}
template<aggregate T>
constexpr auto as_tuple(T const& data) {
constexpr std::size_t fieldCount = count_members<T>();
if constexpr(fieldCount == 0) {
return std::tuple();
} else if constexpr (fieldCount == 1) {
auto& [m1] = data;
return std::tuple(m1);
} else if constexpr (fieldCount == 2) {
auto& [m1, m2] = data;
return std::tuple(m1, m2);
} else if constexpr (fieldCount == 3) {
auto& [m1, m2, m3] = data;
return std::tuple(m1, m2, m3);
} else if constexpr (fieldCount == 4) {
auto& [m1, m2, m3, m4] = data;
return std::tuple(m1, m2, m3, m4);
} else {
static_assert(fieldCount!=fieldCount, "Too many fields for as_tuple(...)! add more if statements!");
}
}
int main() {
struct toto{ int i; };
constexpr toto t{42};
constexpr auto tup = as_tuple(t);
static_assert(std::same_as<const int, std::tuple_element_t<0, decltype(tup)>>);
static_assert(42 == std::get<0>(tup));
}
然后我们需要做的就是生成 if constexpr
个分支,达到您要支持的最大成员数量。
注意:如果你愿意你也可以用std::tie(...)
替换std::tuple(...)
到return一个引用元组到原始成员而不是复制值的元组。 (godbolt example)
2。生成分支
2.1 使用 Boost Preprocessor
Boost Preprocessor 提供了很多方便的宏,我们可以使用它们轻松生成所需的分支。
在这个例子中我们只需要其中的两个宏:
BOOST_PP_ENUM_PARAMS
生成变量名。
示例:BOOST_PP_ENUM_PARAMS(3, foo)
将扩展为 foo0, foo1, foo2
BOOST_PP_REPEAT_FROM_TO
重复调用我们的宏。
示例:BOOST_PP_REPEAT_FROM_TO(1, 3, FOO, ~)
将扩展为 FOO(z,1,~) FOO(z,2,~) FOO(z,3,~)
这使我们仅需几行代码就可以生成多达 255 个 if constexpr
:
增强 PP 示例:godbolt
template<aggregate T>
constexpr auto as_tuple(T& data) {
constexpr std::size_t fieldCount = count_members<T>();
if constexpr(fieldCount == 0) {
return std::tuple();
}
#define AS_TUPLE_STMT(z, n, unused) \
else if constexpr(fieldCount == n) { \
auto& [ BOOST_PP_ENUM_PARAMS(n, m) ] = data; \
return std::tuple( BOOST_PP_ENUM_PARAMS(n, m) ); \
}
BOOST_PP_REPEAT_FROM_TO(1, BOOST_PP_LIMIT_REPEAT, AS_TUPLE_STMT, ~)
#undef AS_TUPLE_STMT
else {
static_assert(fieldCount!=fieldCount, "Too many fields for as_tuple(...)! add more if statements!");
}
}
此版本可以将最多包含 255 个成员的聚合转换为元组。
如果您需要更多,您可以将 BOOST_PP_LIMIT_MAG
& BOOST_PP_LIMIT_REPEAT
更改为 512 或 1024,这将允许您分别处理最多 511 或 1023 个成员的聚合。
2.2 纯 C++ 宏(无提升)
如果没有 boost,您将不得不手动编写大量样板宏代码 - 不幸的是,没有办法解决这个问题。
此示例使用 deferred expressions 和重复扫描(EXPAND
宏)的宏“递归”
C++20 还添加了 __VA_OPT__
宏,这使得使用它变得更加容易。
示例:godbolt
#define NUMBER_SEQ \
50,49,48,47,46,45,44,43,42,41, \
40,39,38,37,36,35,34,33,32,31, \
30,29,28,27,26,25,24,23,22,21, \
20,19,18,17,16,15,14,13,12,11, \
10, 9, 8, 7, 6, 5, 4, 3, 2, 1
#define PARENS ()
#define UNWRAP(...) __VA_ARGS__
#define FIRST(el, ...) el
#define EXPAND(...) EXPAND4(EXPAND4(EXPAND4(EXPAND4(__VA_ARGS__))))
#define EXPAND4(...) EXPAND3(EXPAND3(EXPAND3(EXPAND3(__VA_ARGS__))))
#define EXPAND3(...) EXPAND2(EXPAND2(EXPAND2(EXPAND2(__VA_ARGS__))))
#define EXPAND2(...) EXPAND1(EXPAND1(EXPAND1(EXPAND1(__VA_ARGS__))))
#define EXPAND1(...) __VA_ARGS__
#define SEQ_MAP(macro, ...) __VA_OPT__(EXPAND(SEQ_MAP_HELPER(macro, __VA_ARGS__)))
#define SEQ_MAP_HELPER(macro, el, ...) macro(el __VA_OPT__(, __VA_ARGS__)) __VA_OPT__(SEQ_MAP_HELPER_AGAIN PARENS (macro, __VA_ARGS__))
#define SEQ_MAP_HELPER_AGAIN() SEQ_MAP_HELPER
#define EXPANDZ(...) EXPANDZ4(EXPANDZ4(EXPANDZ4(EXPANDZ4(__VA_ARGS__))))
#define EXPANDZ4(...) EXPANDZ3(EXPANDZ3(EXPANDZ3(EXPANDZ3(__VA_ARGS__))))
#define EXPANDZ3(...) EXPANDZ2(EXPANDZ2(EXPANDZ2(EXPANDZ2(__VA_ARGS__))))
#define EXPANDZ2(...) EXPANDZ1(EXPANDZ1(EXPANDZ1(EXPANDZ1(__VA_ARGS__))))
#define EXPANDZ1(...) __VA_ARGS__
#define SEQ_MAPZ(macro, ...) __VA_OPT__(EXPANDZ(SEQ_MAPZ_HELPER(macro, __VA_ARGS__)))
#define SEQ_MAPZ_HELPER(macro, el, ...) macro(el __VA_OPT__(, __VA_ARGS__)) __VA_OPT__(, SEQ_MAPZ_HELPER_AGAIN PARENS (macro, __VA_ARGS__))
#define SEQ_MAPZ_HELPER_AGAIN() SEQ_MAPZ_HELPER
#define ADD_M(x, ...) m##x
#define GEN_BRANCH(...) \
else if constexpr(fieldCount == FIRST(__VA_ARGS__)) { \
auto& [ SEQ_MAPZ(ADD_M, __VA_ARGS__) ] = data; \
return std::tuple( SEQ_MAPZ(ADD_M, __VA_ARGS__) ); \
}
template<aggregate T>
constexpr auto as_tuple(T& data) {
constexpr std::size_t fieldCount = count_members<T>();
if constexpr(fieldCount == 0) {
return std::tuple();
}
SEQ_MAP(GEN_BRANCH, NUMBER_SEQ)
else {
static_assert(fieldCount!=fieldCount, "Too many fields for as_tuple(...)! add more if statements!");
}
}
此版本最多支持 50 个成员,但您可以根据需要向 NUMBER_SEQ
添加更多数字并添加更多 EXPANDx
宏(每个 EXPANDx
宏你添加四倍的扫描次数,所以你只需要其中的几个)
推荐读物:
- Recursive Macros with C++20 __VA_OPT__
- C Preprocessor tricks, tips and idioms
- How to expand a recursive macro via __VA_OPT__ in a nested context
- Recursive macros via __VA_OPT__
2.3 编写代码生成器
除了使用宏生成代码,您还可以编写一个程序,为您的实际程序生成 header 作为 build-setup.
的一部分
这使代码更易于阅读(没有宏恶作剧)- 您只需为要支持的成员数量生成一次代码。
示例:godbolt
#include <iostream>
#include <vector>
int main() {
std::cout << R"(
#include <tuple>
#include <concepts>
template<class T>
concept aggregate = std::is_aggregate_v<T>;
struct any_type {
template<class T>
operator T() {}
};
template<aggregate T>
consteval std::size_t count_members(auto ...members) {
if constexpr (!requires { T{ members... }; })
return sizeof...(members) - 1;
else
return count_members<T>(members..., any_type{});
}
template<aggregate T>
constexpr auto as_tuple(T const& data) {
constexpr std::size_t fieldCount = count_members<T>();
if constexpr(fieldCount == 0) {
return std::tuple();
}
)";
std::string variables;
for(int i = 1; i <= 10; i++) {
if(variables.length() > 0) variables += ", ";
variables += "m" + std::to_string(i);
std::cout
<< " else if constexpr(fieldCount == " << i << ") {\n"
<< " auto& [" << variables << "] = data;\n"
<< " return std::tuple(" << variables << ");\n"
<< " }\n";
}
std::cout
<< " else {\n"
<< " static_assert(fieldCount!=fieldCount, \"Too many fields for as_tuple(...)! add more if statements!\");\n"
<< " }\n"
<< "}"
<< std::endl;
return 0;
}
3。现有实施:Boost PFR
您可能想查看 Boost PFR - 它完全符合您的要求:一个 tuple-like 任意结构的接口:
Boost.PFR is a C++14 library for a very basic reflection. It gives you access to structure elements by index and provides other std::tuple like methods for user defined types without macro or boilerplate code
示例:godbolt
#include <boost/pfr.hpp>
int main() {
struct foo { char a; int b; };
constexpr foo var{'A', 42};
// converting to tuple
constexpr auto tup = boost::pfr::structure_to_tuple(var);
static_assert(std::same_as<const char, std::tuple_element_t<0, decltype(tup)>>);
static_assert(std::get<0>(tup) == 'A');
static_assert(std::get<1>(tup) == 42);
// direct
static_assert(boost::pfr::get<0>(var) == 'A');
static_assert(boost::pfr::get<1>(var) == 42);
}
有没有办法使用具有任意数量身份的结构化绑定?
P1061 "Structured Bindings can introduce a Pack" 提供了一个方便的解决方案,但目前还不可用。
我想要实现的是为聚合类型提供类似元组的接口。
(std::get<N>(T)
, std::tuple_element_t<T>
, 等等).
我已经有了一个计算字段的函数,所以我现在正在寻找的是一种 - 甚至是棘手的方法 - 来实现以下内容:
template <std::size_t N>
constexpr auto as_tuple(auto && value) noexcept {
auto & [ /* N identities ...*/ ] = value;
return std::tuple/* of references */{ /* N identities... */ };
}
充满绝望,我尝试了一些使用预处理器的想法(免责声明:不是我的那杯茶),没有可扩展的结果。
// 8 bits [0..255]
#define PP_IDENTITY_BIT_1(...) id_##__VA_ARGS__
#define PP_IDENTITY_BIT_2(...) PP_IDENTITY_BIT_1(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_1(__VA_ARGS__ ## 1)
#define PP_IDENTITY_BIT_3(...) PP_IDENTITY_BIT_2(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_2(__VA_ARGS__ ## 1)
#define PP_IDENTITY_BIT_4(...) PP_IDENTITY_BIT_3(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_3(__VA_ARGS__ ## 1)
#define PP_IDENTITY_BIT_5(...) PP_IDENTITY_BIT_4(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_4(__VA_ARGS__ ## 1)
#define PP_IDENTITY_BIT_6(...) PP_IDENTITY_BIT_5(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_5(__VA_ARGS__ ## 1)
#define PP_IDENTITY_BIT_7(...) PP_IDENTITY_BIT_6(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_6(__VA_ARGS__ ## 1)
#define PP_IDENTITY_BIT_8(...) PP_IDENTITY_BIT_7(__VA_ARGS__ ## 0), PP_IDENTITY_BIT_7(__VA_ARGS__ ## 1)
// key idea : conditionaly expand macros, based on bitwise check,
// like `(N & 1) != 0` -> `PP_IDENTITY_BIT_1`
不幸的是,从 C++20 开始,结构化绑定不支持参数包。
你可以通过为每个不同数量的参数提供一个实现来解决这个问题(就像你的例子一样)
1。使用 if constexpr
您可以通过使用 if constexpr
稍微简化它 - 如果条件不为真,则整个语句将被丢弃(并且对于未实例化的模板化实体,即它不必编译给定的模板类型)。
auto
return 类型仅从 non-discarded return 语句推断出 return 类型,因此您可以使用 if constexpr
更改 return函数类型。
这包括:
If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity, if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.
9.2.9.6.1 (3) Placeholder type specifiers
If the declared return type of the function contains a placeholder type, the return type of the function is deduced from non-discarded return statements, if any, in the body of the function.
这允许您像这样编写 as_tuple
函数:
示例:godbolt
#include <tuple>
#include <concepts>
template<class T>
concept aggregate = std::is_aggregate_v<T>;
struct any_type {
template<class T>
operator T() {}
};
// Count the number of members in an aggregate by (ab-)using
// aggregate initialization
template<aggregate T>
consteval std::size_t count_members(auto ...members) {
if constexpr (!requires { T{ members... }; })
return sizeof...(members) - 1;
else
return count_members<T>(members..., any_type{});
}
template<aggregate T>
constexpr auto as_tuple(T const& data) {
constexpr std::size_t fieldCount = count_members<T>();
if constexpr(fieldCount == 0) {
return std::tuple();
} else if constexpr (fieldCount == 1) {
auto& [m1] = data;
return std::tuple(m1);
} else if constexpr (fieldCount == 2) {
auto& [m1, m2] = data;
return std::tuple(m1, m2);
} else if constexpr (fieldCount == 3) {
auto& [m1, m2, m3] = data;
return std::tuple(m1, m2, m3);
} else if constexpr (fieldCount == 4) {
auto& [m1, m2, m3, m4] = data;
return std::tuple(m1, m2, m3, m4);
} else {
static_assert(fieldCount!=fieldCount, "Too many fields for as_tuple(...)! add more if statements!");
}
}
int main() {
struct toto{ int i; };
constexpr toto t{42};
constexpr auto tup = as_tuple(t);
static_assert(std::same_as<const int, std::tuple_element_t<0, decltype(tup)>>);
static_assert(42 == std::get<0>(tup));
}
然后我们需要做的就是生成 if constexpr
个分支,达到您要支持的最大成员数量。
注意:如果你愿意你也可以用std::tie(...)
替换std::tuple(...)
到return一个引用元组到原始成员而不是复制值的元组。 (godbolt example)
2。生成分支
2.1 使用 Boost Preprocessor
Boost Preprocessor 提供了很多方便的宏,我们可以使用它们轻松生成所需的分支。
在这个例子中我们只需要其中的两个宏:
BOOST_PP_ENUM_PARAMS
生成变量名。
示例:BOOST_PP_ENUM_PARAMS(3, foo)
将扩展为foo0, foo1, foo2
BOOST_PP_REPEAT_FROM_TO
重复调用我们的宏。
示例:BOOST_PP_REPEAT_FROM_TO(1, 3, FOO, ~)
将扩展为FOO(z,1,~) FOO(z,2,~) FOO(z,3,~)
这使我们仅需几行代码就可以生成多达 255 个 if constexpr
:
增强 PP 示例:godbolt
template<aggregate T>
constexpr auto as_tuple(T& data) {
constexpr std::size_t fieldCount = count_members<T>();
if constexpr(fieldCount == 0) {
return std::tuple();
}
#define AS_TUPLE_STMT(z, n, unused) \
else if constexpr(fieldCount == n) { \
auto& [ BOOST_PP_ENUM_PARAMS(n, m) ] = data; \
return std::tuple( BOOST_PP_ENUM_PARAMS(n, m) ); \
}
BOOST_PP_REPEAT_FROM_TO(1, BOOST_PP_LIMIT_REPEAT, AS_TUPLE_STMT, ~)
#undef AS_TUPLE_STMT
else {
static_assert(fieldCount!=fieldCount, "Too many fields for as_tuple(...)! add more if statements!");
}
}
此版本可以将最多包含 255 个成员的聚合转换为元组。
如果您需要更多,您可以将 BOOST_PP_LIMIT_MAG
& BOOST_PP_LIMIT_REPEAT
更改为 512 或 1024,这将允许您分别处理最多 511 或 1023 个成员的聚合。
2.2 纯 C++ 宏(无提升)
如果没有 boost,您将不得不手动编写大量样板宏代码 - 不幸的是,没有办法解决这个问题。
此示例使用 deferred expressions 和重复扫描(EXPAND
宏)的宏“递归”
C++20 还添加了 __VA_OPT__
宏,这使得使用它变得更加容易。
示例:godbolt
#define NUMBER_SEQ \
50,49,48,47,46,45,44,43,42,41, \
40,39,38,37,36,35,34,33,32,31, \
30,29,28,27,26,25,24,23,22,21, \
20,19,18,17,16,15,14,13,12,11, \
10, 9, 8, 7, 6, 5, 4, 3, 2, 1
#define PARENS ()
#define UNWRAP(...) __VA_ARGS__
#define FIRST(el, ...) el
#define EXPAND(...) EXPAND4(EXPAND4(EXPAND4(EXPAND4(__VA_ARGS__))))
#define EXPAND4(...) EXPAND3(EXPAND3(EXPAND3(EXPAND3(__VA_ARGS__))))
#define EXPAND3(...) EXPAND2(EXPAND2(EXPAND2(EXPAND2(__VA_ARGS__))))
#define EXPAND2(...) EXPAND1(EXPAND1(EXPAND1(EXPAND1(__VA_ARGS__))))
#define EXPAND1(...) __VA_ARGS__
#define SEQ_MAP(macro, ...) __VA_OPT__(EXPAND(SEQ_MAP_HELPER(macro, __VA_ARGS__)))
#define SEQ_MAP_HELPER(macro, el, ...) macro(el __VA_OPT__(, __VA_ARGS__)) __VA_OPT__(SEQ_MAP_HELPER_AGAIN PARENS (macro, __VA_ARGS__))
#define SEQ_MAP_HELPER_AGAIN() SEQ_MAP_HELPER
#define EXPANDZ(...) EXPANDZ4(EXPANDZ4(EXPANDZ4(EXPANDZ4(__VA_ARGS__))))
#define EXPANDZ4(...) EXPANDZ3(EXPANDZ3(EXPANDZ3(EXPANDZ3(__VA_ARGS__))))
#define EXPANDZ3(...) EXPANDZ2(EXPANDZ2(EXPANDZ2(EXPANDZ2(__VA_ARGS__))))
#define EXPANDZ2(...) EXPANDZ1(EXPANDZ1(EXPANDZ1(EXPANDZ1(__VA_ARGS__))))
#define EXPANDZ1(...) __VA_ARGS__
#define SEQ_MAPZ(macro, ...) __VA_OPT__(EXPANDZ(SEQ_MAPZ_HELPER(macro, __VA_ARGS__)))
#define SEQ_MAPZ_HELPER(macro, el, ...) macro(el __VA_OPT__(, __VA_ARGS__)) __VA_OPT__(, SEQ_MAPZ_HELPER_AGAIN PARENS (macro, __VA_ARGS__))
#define SEQ_MAPZ_HELPER_AGAIN() SEQ_MAPZ_HELPER
#define ADD_M(x, ...) m##x
#define GEN_BRANCH(...) \
else if constexpr(fieldCount == FIRST(__VA_ARGS__)) { \
auto& [ SEQ_MAPZ(ADD_M, __VA_ARGS__) ] = data; \
return std::tuple( SEQ_MAPZ(ADD_M, __VA_ARGS__) ); \
}
template<aggregate T>
constexpr auto as_tuple(T& data) {
constexpr std::size_t fieldCount = count_members<T>();
if constexpr(fieldCount == 0) {
return std::tuple();
}
SEQ_MAP(GEN_BRANCH, NUMBER_SEQ)
else {
static_assert(fieldCount!=fieldCount, "Too many fields for as_tuple(...)! add more if statements!");
}
}
此版本最多支持 50 个成员,但您可以根据需要向 NUMBER_SEQ
添加更多数字并添加更多 EXPANDx
宏(每个 EXPANDx
宏你添加四倍的扫描次数,所以你只需要其中的几个)
推荐读物:
- Recursive Macros with C++20 __VA_OPT__
- C Preprocessor tricks, tips and idioms
- How to expand a recursive macro via __VA_OPT__ in a nested context
- Recursive macros via __VA_OPT__
2.3 编写代码生成器
除了使用宏生成代码,您还可以编写一个程序,为您的实际程序生成 header 作为 build-setup.
的一部分这使代码更易于阅读(没有宏恶作剧)- 您只需为要支持的成员数量生成一次代码。
示例:godbolt
#include <iostream>
#include <vector>
int main() {
std::cout << R"(
#include <tuple>
#include <concepts>
template<class T>
concept aggregate = std::is_aggregate_v<T>;
struct any_type {
template<class T>
operator T() {}
};
template<aggregate T>
consteval std::size_t count_members(auto ...members) {
if constexpr (!requires { T{ members... }; })
return sizeof...(members) - 1;
else
return count_members<T>(members..., any_type{});
}
template<aggregate T>
constexpr auto as_tuple(T const& data) {
constexpr std::size_t fieldCount = count_members<T>();
if constexpr(fieldCount == 0) {
return std::tuple();
}
)";
std::string variables;
for(int i = 1; i <= 10; i++) {
if(variables.length() > 0) variables += ", ";
variables += "m" + std::to_string(i);
std::cout
<< " else if constexpr(fieldCount == " << i << ") {\n"
<< " auto& [" << variables << "] = data;\n"
<< " return std::tuple(" << variables << ");\n"
<< " }\n";
}
std::cout
<< " else {\n"
<< " static_assert(fieldCount!=fieldCount, \"Too many fields for as_tuple(...)! add more if statements!\");\n"
<< " }\n"
<< "}"
<< std::endl;
return 0;
}
3。现有实施:Boost PFR
您可能想查看 Boost PFR - 它完全符合您的要求:一个 tuple-like 任意结构的接口:
Boost.PFR is a C++14 library for a very basic reflection. It gives you access to structure elements by index and provides other std::tuple like methods for user defined types without macro or boilerplate code
示例:godbolt
#include <boost/pfr.hpp>
int main() {
struct foo { char a; int b; };
constexpr foo var{'A', 42};
// converting to tuple
constexpr auto tup = boost::pfr::structure_to_tuple(var);
static_assert(std::same_as<const char, std::tuple_element_t<0, decltype(tup)>>);
static_assert(std::get<0>(tup) == 'A');
static_assert(std::get<1>(tup) == 42);
// direct
static_assert(boost::pfr::get<0>(var) == 'A');
static_assert(boost::pfr::get<1>(var) == 42);
}