2012-12-12 14 views
8

ho un seguito struct:simulare tempo di compilazione riflessione in C++

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

voglio selezionare uno dei membri per nome stringa in constexpr maniera, come

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

getMember è constexpr funzione/struct/macros/qualunque cosa in questione ed espressione dovrebbe essere (voglio che sia) ottimizzata in semplice auto& member = instance.firstMember;. Il mio desiderio qui è di essere in grado di chiamare getMember da un'altra funzione constexpr, che a sua volta sta calcolando il nome di un particolare membro -> una sorta di riflessione sul tempo di compilazione.

lo so, non v'è alcuna riflessione in C++, quindi è OK per registrare qualche modo (parzialmente specializzarsi usare qualche magia macro??) I nomi dei membri della struct in questione, come:

REGISTER_MEMBER(Data, "firstMember", firstMember); 

Tutto quello che voglio è quello di avere l'ottimizzazione del tempo di compilazione e non fare nulla in runtime. È possibile in C++ 11 e come?

+2

Forse [boost :: fusion] (http://www.boost.org/doc/libs/1_52_0/libs/fusion/doc/html/index.html) può aiutare qui? –

+0

@AlexandreC., Potresti essere più specifico? Boost :: fusion è una grande libreria :) Inoltre, voglio reiterare, non voglio avere alcun sovraccarico di runtime, compoto solo i calcoli del tempo. –

+0

@ hate-engine: vedi 'BOOST_FUSION_ADAPT_STRUCT', che è effettivamente quello che stai codificando. – GManNickG

risposta

13

Come notato nei commenti, in primo luogo dare un'occhiata a BOOST_FUSION_ADAPT_STRUCT (e amici):

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

Questo trasforma la struttura Data in una sequenza utilizzabile da 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; 
} 

Questo stampa "firstData" . Cambia l'indice per fare riferimento ai membri in ordine.

Lì, ora possiamo fare riferimento ai membri in fase di compilazione utilizzando un numero. Ma tu volevi un nome. Inoltre, nei commenti, l'elaborazione delle stringhe è una funzione di runtime ... quasi. C++ 11 ci dà constexpr.

E 'un po' complicato, ma alla fine sembra che questo:

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

Sembra spaventoso ma la sua leggibile se si prende il tempo di scegliere lo distingue.

Dobbiamo introdurre i nostri macro per fornire l'accesso di espressioni costanti ai nomi dei membri; la maggior parte del brutto viene dall'elaborazione degli elenchi Boost.Preprocessor. Anche se Fusion registra i nomi durante l'adattamento (vedi boost::fusion::extension::struct_member_name), non sono contrassegnati come constexpr quindi non sono utilizzabili da noi, sfortunatamente.

Questo dà:

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

Che credo sia vicino a quello che cercavamo.

Ma tieni presente che potrebbe non essere necessario: Boost.Fusion potrebbe già disporre di ciò di cui hai bisogno. Vive nell'area tra roba di compilazione pura (Boost.MPL) e roba di run-time regolare; adattare la tua struttura e puoi già fare cose come iterare su di essa (boost::fusion::for_each).

+0

Perfetto! Grazie mille! –

Problemi correlati