2012-12-12 11 views
8

Mam następujące struct:symulacji skompilować odbicie czasu w C++

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

Chcę wybrać jeden z członków wg nazwy smyczkowy constexpr sposób, jak

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

getMember jest constexpr funkcji/struct/makra/cokolwiek w pytaniu i wypowiedzi powinny być (chcę go mieć) zoptymalizowane do prostego auto& member = instance.firstMember;. Moim pragnieniem jest, aby być w stanie wywołać getMember z innego constexpr funkcji, które z kolei są obliczeniowej nazwę konkretnego użytkownika -> jakiś rodzaj kompilacji refleksji czasu.

wiem, nie ma odbicia w C++, więc jest OK jakoś zarejestrować (częściowo specjalizują się korzystać z niektórych makr magię?) Nazwiska członków struktury w kwestii, jak:

REGISTER_MEMBER(Data, "firstMember", firstMember); 

All I Want jest optymalizacja czasu kompilacji i nie rób nic w czasie wykonywania. Czy to możliwe w C++ 11 i jak?

+2

Może [boost :: Fusion] (http://www.boost.org/doc/libs/1_52_0/libs/fusion/doc/html/index.html) może tu pomóc? –

+0

@AlexandreC., Czy mógłbyś być bardziej konkretny? Boost :: fusion jest dużą biblioteką :) Ponadto, chcę powtórzyć, nie chcę mieć żadnego runtime overhead, tylko kompilować obliczenia czasu. –

+0

@ Hate-engine: Zobacz 'BOOST_FUSION_ADAPT_STRUCT', który jest faktycznie tym, co kodujesz. – GManNickG

Odpowiedz

13

Jak zauważył w komentarzach, najpierw przyjrzeć BOOST_FUSION_ADAPT_STRUCT (i przyjaciół):

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

ta zamienia strukturę Data w sekwencji użytkowej przez 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; 
} 

Drukuje "firstData" . Zmień indeks, aby odnieść się do członków w kolejności.

Tam teraz możemy odnieść się do członków w czasie kompilacji przy użyciu numeru. Ale chciałeś mieć imię. Zauważono również w komentarzach, ciągi przetwarzania jest funkcja runtime ... prawie. C++ 11 daje nam constexpr.

To trochę skomplikowane, ale ostatecznie wygląda tak:

#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 : 

Wygląda groźnie, ale jego czytelny, jeśli masz trochę czasu, żeby ją od siebie.

Musimy wprowadzić własne makra w celu umożliwienia dostępu do stałej ekspresji nazw członkowskich; większość brzydkich pochodzi z przetwarzania list Boost.Preprocessor. Chociaż Fusion robi rejestrować nazwy podczas adaptacji, jak również (patrz boost::fusion::extension::struct_member_name), nie są one oznaczone jako constexpr więc nie są użyteczne dla nas, niestety.

Daje:

#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; */ 
} 

co moim zdaniem jest blisko do tego, co było potem.

Należy jednak pamiętać, że to nie wszystko może być konieczne: Boost.Fusion może już mieć to, czego potrzebujesz. Żyje w obszarze pomiędzy czystymi kompilacjami (Boost.MPL) i zwykłymi rzeczami wykonawczymi; dostosuj swoją strukturę i już możesz robić iteracje nad nią (boost::fusion::for_each).

+0

Idealny! Dziękuję Ci bardzo! –

Powiązane problemy