constexpr — симулировать отражение времени компиляции в переполнении стека

У меня есть следующая структура:

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

Я хочу выбрать одного из членов по имени строки в constexpr манера, как

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

getMember is constexpr Функция / структура / макросы / все, что в вопросе и выражении должно быть (я хочу, чтобы) было оптимизировано в простой auto& member = instance.firstMember;, Мое желание здесь, чтобы иметь возможность позвонить getMember От другого constexpr функция, которая в свою очередь вычисляет имя конкретного члена -> своего рода отражение времени компиляции.

Я знаю, что нет никакого отражения в C ++, поэтому все в порядке, чтобы зарегистрировать (частично специализировать? Использовать некоторые макросы магии?) Имена членов рассматриваемой структуры, например:

REGISTER_MEMBER(Data, "firstMember", firstMember);

Все, что я хочу — это оптимизировать время компиляции и ничего не делать во время выполнения. Это возможно в C ++ 11 и как?

8

Решение

Как отмечено в комментариях, сначала взгляните на BOOST_FUSION_ADAPT_STRUCT (и друзья):

#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).

13

Другие решения

Других решений пока нет …

А ты уже прошел курс программирования? Супер скидка!
Прокачать скилл $$$
×