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
).
Może [boost :: Fusion] (http://www.boost.org/doc/libs/1_52_0/libs/fusion/doc/html/index.html) może tu pomóc? –
@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. –
@ Hate-engine: Zobacz 'BOOST_FUSION_ADAPT_STRUCT', który jest faktycznie tym, co kodujesz. – GManNickG