आपको जो करने की आवश्यकता है वह है प्रीप्रोसेसर फ़ील्ड के बारे में प्रतिबिंब डेटा उत्पन्न करना। यह डेटा नेस्टेड कक्षाओं के रूप में संग्रहीत किया जा सकता है।
सबसे पहले, इसे आसान बनाने के लिए और क्लीनर बनाने के लिए इसे प्रीप्रोसेसर में हम टाइप की गई अभिव्यक्ति का उपयोग करेंगे। एक टाइप की गई अभिव्यक्ति सिर्फ एक अभिव्यक्ति है जो टाइप को कोष्ठक में रखती है। इसलिए लिखने के बजाय int x
आप लिखेंगे (int) x
। टाइप किए गए भावों की मदद के लिए यहां कुछ आसान मैक्रो हैं:
#define REM(...) __VA_ARGS__
#define EAT(...)
// Retrieve the type
#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
// Strip off the type
#define STRIP(x) EAT x
// Show the type without parenthesis
#define PAIR(x) REM x
इसके बाद, हम REFLECTABLE
प्रत्येक फ़ील्ड (साथ ही फ़ील्ड) के बारे में डेटा उत्पन्न करने के लिए एक मैक्रो को परिभाषित करते हैं । इस मैक्रो को इस तरह कहा जाएगा:
REFLECTABLE
(
(const char *) name,
(int) age
)
इसलिए Boost.PP का उपयोग करके हम प्रत्येक तर्क पर पुनरावृति करते हैं और इस तरह से डेटा उत्पन्न करते हैं:
// A helper metafunction for adding const to a type
template<class M, class T>
struct make_const
{
typedef T type;
};
template<class M, class T>
struct make_const<const M, T>
{
typedef typename boost::add_const<T>::type type;
};
#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
Self & self; \
field_data(Self & self) : self(self) {} \
\
typename make_const<Self, TYPEOF(x)>::type & get() \
{ \
return self.STRIP(x); \
}\
typename boost::add_const<TYPEOF(x)>::type & get() const \
{ \
return self.STRIP(x); \
}\
const char * name() const \
{\
return BOOST_PP_STRINGIZE(STRIP(x)); \
} \
}; \
यह जो करता है वह एक स्थिरांक उत्पन्न करता है fields_n
जो कक्षा में परावर्तनीय क्षेत्रों की संख्या है। फिर यह field_data
प्रत्येक क्षेत्र के लिए माहिर है । यह reflector
कक्षा को भी मित्र बनाता है, इसलिए यह निजी होने पर भी खेतों तक पहुंच सकता है:
struct reflector
{
//Get field_data at index N
template<int N, class T>
static typename T::template field_data<N, T> get_field_data(T& x)
{
return typename T::template field_data<N, T>(x);
}
// Get the number of fields
template<class T>
struct fields
{
static const int n = T::fields_n;
};
};
अब हम विज़िटर पैटर्न का उपयोग करने वाले क्षेत्रों पर पुनरावृति करने के लिए। हम 0 से लेकर कई फ़ील्ड तक एक MPL रेंज बनाते हैं, और उस इंडेक्स पर फ़ील्ड डेटा एक्सेस करते हैं। फिर यह उपयोगकर्ता द्वारा प्रदान किए गए विज़िटर पर फ़ील्ड डेटा पास करता है:
struct field_visitor
{
template<class C, class Visitor, class I>
void operator()(C& c, Visitor v, I)
{
v(reflector::get_field_data<I::value>(c));
}
};
template<class C, class Visitor>
void visit_each(C & c, Visitor v)
{
typedef boost::mpl::range_c<int,0,reflector::fields<C>::n> range;
boost::mpl::for_each<range>(boost::bind<void>(field_visitor(), boost::ref(c), v, _1));
}
अब सच्चाई के क्षण के लिए हमने इसे एक साथ रखा है। यहाँ बताया गया है कि हम किस Person
प्रकार एक वर्ग को परिभाषित कर सकते हैं :
struct Person
{
Person(const char *name, int age)
:
name(name),
age(age)
{
}
private:
REFLECTABLE
(
(const char *) name,
(int) age
)
};
यहाँ print_fields
फ़ील्ड्स पर पुनरावृति करने के लिए प्रतिबिंब डेटा का उपयोग करके एक सामान्यीकृत फ़ंक्शन है:
struct print_visitor
{
template<class FieldData>
void operator()(FieldData f)
{
std::cout << f.name() << "=" << f.get() << std::endl;
}
};
template<class T>
void print_fields(T & x)
{
visit_each(x, print_visitor());
}
print_fields
परावर्तक Person
वर्ग के साथ उपयोग करने का एक उदाहरण :
int main()
{
Person p("Tom", 82);
print_fields(p);
return 0;
}
कौन से आउटपुट:
name=Tom
age=82
और वॉइला, हमने कोड की 100 लाइनों के तहत, C ++ में प्रतिबिंब लागू किया है।