Сразу пример с несколькими сценариями использования:
struct A
{
// Определяем в структуре 3 поля
// int int_field_;
// std::string string_field_;
// double no_ann_field_;
// Первые два поля имеют аннотации, последнее нет.
AUX_DECLARE_AND_ANNOTATE(
((int, int_field_,
((int_annotation, 24.0))
((string_annotation, "Privet"))
))
((std::string, string_field_,
((no_serialization, std::true_type()))
((no_hash, std::true_type()))
))
((double, no_ann_field_, BOOST_PP_SEQ_NIL))
)
};
// Определим метафункцию позволяющую определить есть ли в типе с аннотациями, аннотация no_hash
AUX_DEFINE_MEMBER_DETECTOR(no_hash)
// Визитор для boost::fusion::for_each, см. код ниже
struct dump_members
{
template<typename MemberType, typename AnnotationsType>
void operator()(const aux::annotated_member<MemberType, AnnotationsType>& member) const
{
cout << "Member value: " << member.value_ << endl;
// We can make compile time check here whether member has specific annotation
cout << "\tHas no_hash annotation: " << has_no_hash<AnnotationsType>::value << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A a;
// Use case 1:
// Явно обращаемся к члену и к типу содержащему его аннотации
a.int_field_ = 100;
a.string_field_ = "string field";
a.no_ann_field_ = 20.;
A::annotations_for_int_field_ a1;
A::annotations_for_string_field_ a2;
A::annotations_for_no_ann_field_ a3;
cout << "Value of int_annotation for A::int_field_" << a1.int_annotation << endl;
// Use case 2:
// Метафункция проверяющая являются ли поля в типе аннотированными
cout << "has_annotations<A> = " << aux::has_annotations<A>::value << endl;
cout << "has_annotations<int> = " << aux::has_annotations<int>::value << endl;
// Use case 3:
// Получение членов структуры и связанных с ними аннотаций в виде кортежа,
boost::fusion::for_each(a.annotated_tuple(), dump_members());
return 0;
}
А вот реализация
#pragma once
/// @file annotations.hpp
#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/adapted/struct/define_struct_inline.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/comma_if.hpp>
#include <boost/preprocessor/identity.hpp>
#include <boost/preprocessor/empty.hpp>
#include <boost/mpl/has_xxx.hpp>
namespace aux {
/// Bound together
template<typename MemberType, typename AnnotationsType>
struct annotated_member
{
typedef AnnotationsType annotations_type;
typedef MemberType member_type;
MemberType& value_;
annotated_member(MemberType& value) : value_(value) {}
};
//--------------------- USER LEVEL MACROSES --------------------------------------------
/// Declare and annotate member with specified types
/// @code
///struct A
///{
/// AUX_DECLARE_AND_ANNOTATE(
/// ((int, int_field_,
/// ((int_annotation, 24.0))
/// ((string_annotation, "Privet"))
/// ))
/// ((std::string, string_field_,
/// ((no_serialization, std::true_type()))
/// ((no_hash, std::true_type()))
/// ))
/// ((double, no_ann_field_, BOOST_PP_SEQ_NIL))
/// )
//};
/// @endcode
#define AUX_DECLARE_AND_ANNOTATE(X) \
typedef void annotated_tag; \
BOOST_PP_SEQ_FOR_EACH_R(1, AUX_DETAIL_DECLARE_MEMBER, _, X) \
BOOST_PP_SEQ_FOR_EACH_R(1, AUX_DETAIL_DECLARE_ANNOTATION_STRUCT, _, X) \
AUX_DETAIL_DECLARE_MUTABLE_TUPLE_TYPE(X) \
AUX_DETAIL_DECLARE_CONST_TUPLE_TYPE(X)
/// Define metafunction that become true if type member were defined using AUX_DECLARE_AND_ANNOTATE
BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(has_annotations, annotated_tag, false);
/// Stolen from http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector
/// Define metafunction that detect presence of some member in structure
#define AUX_DEFINE_MEMBER_DETECTOR(X) \
template<typename T> class has_##X { \
struct Fallback { int X; }; \
struct Derived : T, Fallback { }; \
\
template<typename U, U> struct Check; \
\
typedef char ArrayOfOne[1]; \
typedef char ArrayOfTwo[2]; \
\
template<typename U> static ArrayOfOne & func(Check<int Fallback::*, &U::X> *); \
template<typename U> static ArrayOfTwo & func(...); \
public: \
typedef has_##X type; \
enum { value = sizeof(func<Derived>(0)) == 2 }; \
};
//--------------------- IMPLEMENTATION LEVEL MACROSES --------------------------------------------
// Define type that will be used for tuple. Better to use C++11 template aliasing instead of macro
#define AUX_DETAIL_TUPLE_TYPE boost::fusion::vector
// Accessors for top level tuple
#define AUX_DETAIL_L1_TYPE(tup) BOOST_PP_TUPLE_ELEM(3, 0, tup)
#define AUX_DETAIL_L1_MEMBER(tup) BOOST_PP_TUPLE_ELEM(3, 1, tup)
#define AUX_DETAIL_L1_ANN_SEQ(tup) BOOST_PP_TUPLE_ELEM(3, 2, tup)
// Accessort for annotations level tuple
#define AUX_DETAIL_L2_NAME(tup) BOOST_PP_TUPLE_ELEM(2, 0, tup)
#define AUX_DETAIL_L2_VALUE(tup) BOOST_PP_TUPLE_ELEM(2, 1, tup)
// Produce name for annotation structure
#define AUX_DETAIL_ANN_NAME(tup) BOOST_PP_CAT(annotations_for_, AUX_DETAIL_L1_MEMBER(tup))
// Declare member using top level tuple
#define AUX_DETAIL_DECLARE_MEMBER(r, data, tup) AUX_DETAIL_L1_TYPE(tup) AUX_DETAIL_L1_MEMBER(tup);
#if !defined(AUX_USE_TUPLE_FOR_ANNOTATIONS)
// Produce colon on false and comma on true
#define AUX_DETAIL_COLON_OR_COMMA(cond) BOOST_PP_IF(cond, BOOST_PP_COMMA, BOOST_PP_IDENTITY(:))()
// Produce annotation initializer list for annotation structure constructor
#define AUX_DETAIL_INITIALIZER_LIST(r, data, i, tup) AUX_DETAIL_COLON_OR_COMMA(i) AUX_DETAIL_L2_NAME(tup)(AUX_DETAIL_L2_VALUE(tup))
// Produce declaration of annotation member
#define AUX_DETAIL_ANNOTATIONS_LIST(r, data, i, tup) decltype(AUX_DETAIL_L2_VALUE(tup)) AUX_DETAIL_L2_NAME(tup);
// Declare annotation struct using tuple
#define AUX_DETAIL_DECLARE_ANNOTATION_STRUCT(r, data, tup) \
struct AUX_DETAIL_ANN_NAME(tup) { \
AUX_DETAIL_ANN_NAME(tup) () \
BOOST_PP_SEQ_FOR_EACH_I_R(r, AUX_DETAIL_INITIALIZER_LIST, _, AUX_DETAIL_L1_ANN_SEQ(tup)) {} \
BOOST_PP_SEQ_FOR_EACH_I_R(r, AUX_DETAIL_ANNOTATIONS_LIST, _, AUX_DETAIL_L1_ANN_SEQ(tup)) \
};
#else
// TODO: Initialize annotations with provided values
#define AUX_DETAIL_TRANSFORM_TO_FUSION_SEQ(d, data, tup) decltype(AUX_DETAIL_L2_VALUE(tup)), AUX_DETAIL_L2_NAME(tup)
#define AUX_DETAIL_DECLARE_ANNOTATION_STRUCT(r, data, tup) \
BOOST_FUSION_DEFINE_STRUCT_INLINE(AUX_DETAIL_ANN_NAME(tup), BOOST_PP_SEQ_TRANSFORM(AUX_DETAIL_TRANSFORM_TO_FUSION_SEQ, _, AUX_DETAIL_L1_ANN_SEQ(tup)))
#endif
// Produce ", member" or "member" if i is 0
#define AUX_DETAIL_ENUM_MEMBERS(r, data, i, tup) BOOST_PP_COMMA_IF(i) AUX_DETAIL_L1_MEMBER(tup)
#define AUX_DETAIL_ANNOTATED_MEMBER(r, const_or_empty, i, tup) BOOST_PP_COMMA_IF(i) aux::annotated_member<const_or_empty() AUX_DETAIL_L1_TYPE(tup), AUX_DETAIL_ANN_NAME(tup)>
#define AUX_DETAIL_DECLARE_MUTABLE_TUPLE_TYPE(X) \
typedef AUX_DETAIL_TUPLE_TYPE<BOOST_PP_SEQ_FOR_EACH_I(AUX_DETAIL_ANNOTATED_MEMBER, BOOST_PP_EMPTY, X)> annotated_tuple_type; \
annotated_tuple_type annotated_tuple() { return annotated_tuple_type(BOOST_PP_SEQ_FOR_EACH_I(AUX_DETAIL_ENUM_MEMBERS, _, X)); }
#define AUX_DETAIL_DECLARE_CONST_TUPLE_TYPE(X) \
typedef AUX_DETAIL_TUPLE_TYPE<BOOST_PP_SEQ_FOR_EACH_I(AUX_DETAIL_ANNOTATED_MEMBER, BOOST_PP_IDENTITY(const), X)> const_annotated_tuple_type; \
const_annotated_tuple_type annotated_tuple() const { return const_annotated_tuple_type(BOOST_PP_SEQ_FOR_EACH_I(AUX_DETAIL_ENUM_MEMBERS, _, X)); }
} // namespace aux