2012-12-12 29 views
8

Tôi có một cấu trúc như sau:mô phỏng thời gian biên dịch phản chiếu trong C++

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

Tôi muốn chọn một trong các thành viên theo tên chuỗi trong constexpr cách, giống như

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

getMember là constexpr chức năng/struct/macro/bất cứ điều gì trong câu hỏi và biểu thức nên được (tôi muốn nó được) tối ưu hóa vào đơn giản auto& member = instance.firstMember;. Mong muốn của tôi ở đây là có thể gọi getMember từ một hàm constexpr khác, mà lần lượt là tên máy tính của thành viên cụ thể -> một số loại phản ánh thời gian biên dịch.

tôi biết, không có phản xạ trong C++, do đó nó là OK để đăng ký bằng cách nào đó (chuyên phần sử dụng một số macro kỳ diệu?) Tên của các thành viên của struct trong câu hỏi, như:

REGISTER_MEMBER(Data, "firstMember", firstMember); 

Tất cả tôi muốn là có tối ưu hóa thời gian biên dịch và không làm gì trong thời gian chạy. Có thể trong C++ 11 và làm thế nào?

+2

Có thể [boost :: fusion] (http://www.boost.org/doc/libs/1_52_0/libs/fusion/doc/html/index.html) có thể trợ giúp ở đây? –

+0

@AlexandreC., Bạn có thể vui lòng cụ thể hơn không? Boost :: fusion là một thư viện lớn :) Ngoài ra, tôi muốn nhắc lại, tôi không muốn có bất kỳ thời gian chạy trên cao, chỉ tính toán thời gian biên dịch. –

+0

@ hate-engine: Xem 'BOOST_FUSION_ADAPT_STRUCT', đó là những gì bạn đang mã hóa một cách hiệu quả. – GManNickG

Trả lời

13

Như đã đề cập trong các ý kiến, trước hết hãy nhìn vào BOOST_FUSION_ADAPT_STRUCT (và bạn bè):

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

này biến cấu trúc Data của bạn thành một chuỗi có thể sử dụng bởi 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; 
} 

này in "firstData" . Thay đổi chỉ mục để tham khảo các thành viên theo thứ tự.

Hiện tại, chúng tôi có thể tham khảo các thành viên tại thời điểm biên dịch bằng cách sử dụng một số. Nhưng bạn muốn có một cái tên. Cũng lưu ý trong các ý kiến, chuỗi xử lý là một tính năng thời gian chạy ... gần như. C++ 11 cho chúng ta constexpr.

Đó là một chút khó khăn, nhưng cuối cùng nó trông như thế này:

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

Có vẻ đáng sợ nhưng có thể đọc được nó nếu bạn dành thời gian để nhặt nó ra xa nhau.

Chúng tôi phải giới thiệu các macro của riêng chúng tôi để cấp quyền truy cập biểu thức liên tục cho tên thành viên; phần lớn xấu xí đến từ việc xử lý danh sách Boost.Preprocessor. Mặc dù Fusion cũng ghi lại tên trong quá trình thích ứng (xem boost::fusion::extension::struct_member_name), nhưng chúng không được đánh dấu là constexpr vì vậy không thể sử dụng được với chúng tôi.

Điều này cho phép:

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

Mà tôi nghĩ là gần với những gì bạn đang theo đuổi.

Nhưng hãy nhớ rằng điều này có thể không cần thiết: Boost.Fusion có thể đã có những gì bạn cần. Nó sống trong vùng giữa các công cụ biên dịch thuần túy (Boost.MPL) và các công cụ chạy thường xuyên; thích ứng với cấu trúc của bạn và bạn đã có thể làm những việc như lặp lại nó (boost::fusion::for_each).

+0

Hoàn hảo! Cảm ơn nhiều! –