имитировать отражение времени компиляции в C ++

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

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;. Мое желание здесь - иметь возможность вызывать getMember из другой constexpr функции, которая, в свою очередь, вычисляет имя конкретного члена -> какое-то отражение времени компиляции.

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

REGISTER_MEMBER(Data, "firstMember", firstMember);

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


person hate-engine    schedule 12.12.2012    source источник
comment
Может быть, boost :: fusion сможет здесь помочь?   -  person Alexandre C.    schedule 12.12.2012
comment
@AlexandreC., Не могли бы вы быть более конкретными? Boost :: fusion - большая библиотека :) Кроме того, я хочу повторить, я не хочу иметь никаких накладных расходов во время выполнения, только вычисления времени компиляции.   -  person hate-engine    schedule 12.12.2012
comment
@ hate-engine: см. BOOST_FUSION_ADAPT_STRUCT, это фактически то, что вы кодируете.   -  person GManNickG    schedule 12.12.2012
comment
Все время компиляции. Используйте BOOST_FUSION_ADAPT_STRUCT чтобы адаптировать вашу структуру к алгоритмам Fusion.   -  person Alexandre C.    schedule 12.12.2012
comment
Если ваш API для этого использует строку, его нельзя оптимизировать во время компиляции. То, что вы хотите, недоступно ни на одном языке.   -  person brian beuning    schedule 12.12.2012
comment
@brianbeuning На самом деле это неправда. D и Haskell поддерживают это, и я ожидаю, что и другие языки тоже.   -  person Cubic    schedule 25.04.2014


Ответы (1)


Как отмечено в комментариях, сначала взгляните на 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).

person GManNickG    schedule 15.12.2012
comment
Идеально! Большое спасибо! - person hate-engine; 16.12.2012