gpt4 book ai didi

c++ - 在 C++ 中模拟编译时反射

转载 作者:可可西里 更新时间:2023-11-01 17:35:34 24 4
gpt4 key购买 nike

我有以下结构:

struct Data
{
std::string firstMember;
std::string secondMember;
std::string thirdMember;
};

我想以 constexpr 方式通过字符串名称选择其中一个成员,例如

Data instance;
auto& member = getMember(instance, "firstMember");

getMember 是 constexpr 函数/结构/宏/任何有问题的表达式,应该(我希望它是)优化为简单的 auto& member = instance.firstMember; .我的愿望是能够从另一个 constexpr 函数调用 getMember,这又是计算特定成员的名称 --> 某种编译时反射。

我知道,在 C++ 中没有反射,因此可以以某种方式注册(部分专门化?使用一些宏魔术?)相关结构成员的名称,例如:

REGISTER_MEMBER(Data, "firstMember", firstMember);

我只想进行编译时优化,而在运行时什么也不做。这在 C++11 中可能吗?如何实现?

最佳答案

如评论中所述,首先看一下 BOOST_FUSION_ADAPT_STRUCT(和 friend ):

#include <boost/fusion/include/adapt_struct.hpp>
#include <string>

struct Data
{
std::string firstMember;
std::string secondMember;
std::string thirdMember;
};

BOOST_FUSION_ADAPT_STRUCT(
Data,
(std::string, firstMember)
(std::string, secondMember)
(std::string, thirdMember)
)

这会将您的 Data 结构转换为 Fusion 可用的序列:

#include <boost/fusion/include/at_c.hpp>

int main()
{
Data d = { "firstData", "secondData", "thirdData" };

std::cout << boost::fusion::at_c<0>(d) << std::endl;
}

这会打印 "firstData"。更改索引以按顺序引用成员。

现在我们可以在编译时使用数字来引用成员。但你想要一个名字。评论中还指出,处理字符串是一项运行时功能......几乎。 C++11 为我们提供了 constexpr

这有点棘手,但最终看起来像这样:

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/seq.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <stdexcept>

// and repeat for BOOST_FUSION_ADAPT_TPL_STRUCT, etc...
#define REFLECT_STRUCT(NAME, ATTRIBUTES) \
REFLECT_STRUCT_DETAIL(NAME, \
ATTRIBUTES, \
BOOST_PP_SEQ_POP_FRONT( \
BOOST_PP_CAT( \
/* warning: uses fusion implementation details: */ \
BOOST_FUSION_ADAPT_STRUCT_FILLER_0(0,0)ATTRIBUTES, \
_END))) \

#define REFLECT_STRUCT_DETAIL(NAME, ATTRIBUTES, WRAPPEDATTRIBUTES) \
BOOST_FUSION_ADAPT_STRUCT(NAME, ATTRIBUTES) \
\
namespace detail \
{ \
namespace BOOST_PP_CAT(reflect_, NAME) \
{ \
template <int N> \
struct member_name; \
\
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_STRUCT_DETAIL_MEMBER_NAME, \
BOOST_PP_EMPTY, \
WRAPPEDATTRIBUTES) \
\
template <int N> \
constexpr bool member_match_index(const std::size_t index, \
const char* const str, \
const std::size_t len) \
{ \
return index == len || \
(member_name<N>::value()[index] == str[index] \
&& member_match_index<N>(index + 1, str, len)); \
} \
\
template <int N> \
constexpr bool member_match(const char* const str, \
const std::size_t len) \
{ \
return len == member_name<N>::value_length \
&& member_match_index<N>(0, str, len); \
} \
\
constexpr int find_member(const char* const str, \
const std::size_t len) \
{ \
return BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(WRAPPEDATTRIBUTES), \
REFLECT_STRUCT_DETAIL_MEMBER_NAME_TEST, \
BOOST_PP_EMPTY) \
throw std::runtime_error("could not find " \
BOOST_PP_STRINGIZE(NAME) \
" member"); \
} \
} \
} \
\
constexpr int BOOST_PP_CAT(indexof_, NAME)(const char* const str, \
const std::size_t len) \
{ \
return detail::BOOST_PP_CAT(reflect_, NAME)::find_member(str, len); \
} \
\
template <std::size_t N> \
constexpr int BOOST_PP_CAT(indexof_, NAME)(const char (&str)[N]) \
{ \
return detail::BOOST_PP_CAT(reflect_, NAME)::find_member(&str[0], N); \
}

#define REFLECT_STRUCT_DETAIL_EXTRACT_NAME(pair) \
BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, pair))

#define REFLECT_STRUCT_DETAIL_MEMBER_NAME(r, data, n, elem) \
REFLECT_STRUCT_DETAIL_MEMBER_NAME_DETAIL(n, REFLECT_STRUCT_DETAIL_EXTRACT_NAME(elem))

#define REFLECT_STRUCT_DETAIL_MEMBER_NAME_DETAIL(n, name) \
template <> \
struct member_name<n> \
{ \
static constexpr std::size_t value_length = sizeof(name); \
typedef const char value_type[value_length]; \
\
static constexpr const value_type& value() \
{ \
return name; \
} \
};

#define REFLECT_STRUCT_DETAIL_MEMBER_NAME_TEST(z, n, text) \
member_match<n>(str, len) ? n :

它看起来很吓人,但如果你花时间把它拆开,它是可读的。

我们必须引入自己的宏来提供对成员名称的常量表达式访问;大多数丑陋的东西来自处理 Boost.Preprocessor 列表。尽管 Fusion 在适配过程中也会记录名称(参见 boost::fusion::extension::struct_member_name),但它们未标记为 constexpr,因此不能用于不幸的是,我们。

这给出:

#include <boost/fusion/include/at_c.hpp>
#include <iostream>
#include <string>

struct Data
{
std::string firstMember;
std::string secondMember;
std::string thirdMember;
};

REFLECT_STRUCT(
Data,
(std::string, firstMember)
(std::string, secondMember)
(std::string, thirdMember)
)

// your desired code:
// (note the use of at_c ensures this is evaluated at comple-time)
#define GETMEMBER(data, member) boost::fusion::at_c<indexof_Data(member)>(data)

int main()
{
Data d = { "firstData", "secondData", "thirdData" };

std::cout << boost::fusion::at_c<indexof_Data("firstMember")>(d) << std::endl;
std::cout << GETMEMBER(d, "secondMember") << std::endl;
std::cout << GETMEMBER(d, "thirdMember") << std::endl;
/* causes error: std::cout << GETMEMBER(d, "nonexistent_member") << std::endl; */
}

我认为这与您所追求的接近。

但请记住,这可能并非都是必需的:Boost.Fusion 可能已经拥有您需要的东西。它位于纯编译时内容 (Boost.MPL) 和常规运行时内容之间;调整你的结构,你已经可以做一些事情,比如迭代它 (boost::fusion::for_each)。

关于c++ - 在 C++ 中模拟编译时反射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13830792/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com