在C ++中模拟编译时反射 [英] simulate compile time reflection in C++
问题描述
我有以下结构:
struct Data
{
std::string firstMember;
std::string secondMember;
std::string thirdMember;
};
我想在 constexpr中通过字符串名称选择一个成员
方式,例如
Data instance;
auto& member = getMember(instance, "firstMember");
getMember是 constexpr
function / struct / macros /无论有什么问题和表达方式,我都希望将其优化为简单的 auto&成员= instance.firstMember;
。我的愿望是能够从另一个 constexpr
函数调用 getMember
,而该函数反过来又在计算特定成员的名称->某种编译时反射。
getMember is constexpr
function/struct/macros/whatever in question and expression should be (I want it to be) optimized into simple auto& member = instance.firstMember;
. My desire here is to be able to call getMember
from another constexpr
function, which in turn are computing name of particular member --> some kind of compile time reflection.
我知道,在C ++中没有反射,因此可以以某种方式注册(部分专门化?使用一些宏魔术吗? )相关结构成员的名称,例如:
I know, there is no reflection in C++, therefore it's OK to register somehow (partially specialize? use some macros magic?) names of members of struct in question, like:
REGISTER_MEMBER(Data, "firstMember", firstMember);
我想要做的就是进行编译时间优化,并且在运行时不执行任何操作。
All I want is to have that compile time optimization and do nothing in runtime. Is that possible in C++11 and how?
推荐答案
如注释中所述,首先看一下 BOOST_FUSION_ADAPT_STRUCT
(和朋友):
As noted in the comments, first take a look at BOOST_FUSION_ADAPT_STRUCT
(and friends):
#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可以使用的序列:
This turns your Data
structure into a sequence usable by 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
。更改索引以按顺序引用成员。
This prints "firstData"
. Change the index to refer to the members in order.
现在,我们可以在编译时使用数字引用成员。但是你想要一个名字。注释中还指出,处理字符串几乎是运行时功能。 C ++ 11为我们提供了 constexpr
。
There, now we can refer to members at compile-time using a number. But you wanted a name. Also noted in the comments, processing strings is a runtime feature...almost. C++11 gives us constexpr
.
这有点棘手,但最终看起来像这样:
It's a bit tricky, but ultimately it looks like this:
#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 :
它看起来很吓人,但如果您花点时间将其拆开,它就会可读。
It looks scary but its readable if you take the time to pick it apart.
我们必须引入自己的宏,以便对成员名进行常量表达式访问;大多数丑陋来自处理Boost.Preprocessor列表。尽管Fusion也会在改编过程中记录名称(请参见 boost :: fusion :: extension :: struct_member_name
),但不会将它们标记为 constexpr
不幸的是对我们来说不可用。
We have to introduce our own macros to give constant-expression access to the member names; most of the ugly comes from processing Boost.Preprocessor lists. Although Fusion does record the names during adaptation as well (see boost::fusion::extension::struct_member_name
), they are not marked as constexpr
so aren't usable to us, unfortunately.
这给出了:
#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; */
}
我认为这与您的追求很接近。
Which I think is close to what you were after.
但请记住,这可能并非全部必要:Boost.Fusion可能已满足您的需求。它位于纯编译时程序段(Boost.MPL)和常规运行时程序段之间的区域。调整您的结构,您就可以进行迭代操作( boost :: fusion :: for_each
)。
But keep in mind this may not all be necessary: Boost.Fusion may already have what you need. It lives in the area between pure compile-time stuff (Boost.MPL) and regular run-time stuff; adapt your struct and you can already do things like iterate over it (boost::fusion::for_each
).
这篇关于在C ++中模拟编译时反射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!