SORU
14 HAZİRAN 2012, PERŞEMBE


C ön işlemci: üye değişken listesi kod tekrarını önlemek

Bir oluşturucu basit başlatılan farklı üye değişkenleri ile bir çok derslik var. İşte bir örnek:

struct Person
{
    Person(const char *name, int age)
        :
        name(name),
        age(age)
    {
    }
private:
    const char *name;
    int age;
};

Her print<>() ilişkili bir işlevi vardır.

template <>
void print<Person>(const Person &person)
{
    std::cout << "name=" << name << "\n";
    std::cout << "age=" << age << "\n";
}

Bu kod parametre listesi dört yerde çoğaltılır beri hata eğilimli. Nasıl bu yinelenmesini önlemek için kodu yeniden olabilir miyim? Basit ve/veya şablonları kullanmak istiyorum.

Örneğin, bu gibi X-besten basit teknik bir şey kullanabilir miyim?

#define ARGUMENTS \
    ARG(const char *, name) \
    ARG(int, age)

struct Person
{
    Person(LIST_TYPE_NAME_COMMA(ARGUMENTS))
       :
       LIST_NAME_INIT(ARGUMENTS)
    {
    }
private:
    LIST_TYPE_NAME_SEMICOLON(ARGUMENTS)
};

template <>
void print<Person>(const Person &person)
{
   LIST_COUT_LINE(ARGUMENTS)
}

#undef ARGUMENTS

Veya daha iyisi, şablon tabanlı bir yaklaşım?

Lütfen bunu yapmak istiyorum neden soru yok, adlandırılmış parametreler ile birden fazla benzer nesneler olarak sonuçlandı tasarım kararları gerekçeli vardır. Parametreleri, performansı artırmak için üye değişkenleri adlandırılmış olması gerekir. Sadece Olası parametreleri ve türleri sadece bir kez listeye olup olmadığını araştırıyorum.

CEVAP
31 Temmuz 2012, Salı


Yapmanız gerekenler bu kadar basit alanları hakkında yansıma verileri oluşturmak zorunda. Bu veri iç içe sınıflar olarak saklanabilir.

İlk olarak, bu kadar basit yazmak için daha kolay ve daha temiz yapmak için yazılan ifadeyi kullanacağız. Yazılı bir ifade sadece parantez içinde yazın koyan bir ifadedir. Bu yüzden int x yazmak yerine (int) x yazacaksınız. Burada yazılan ifadeler ile yardımcı olmak için bazı kullanışlı makrolar:

#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

Sonraki, biz REFLECTABLE makro her alan hakkında veri(artı alan kendisi) oluşturmak için kullanılır. Bu makroyu bu şekilde çağrılır:

REFLECTABLE
(
    (const char *) name,
    (int) age
)

Boost.PP biz her argüman üzerinde yineleme ve verileri oluşturmak kullanarak bu gibi:

// 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)); \
    } \
}; \

Bu ne sınıfta reflectable alanların sayısı fields_n bir sabit üretir. Her alan için field_data uzmanlaşmıştır. Ayrıca reflector sınıf arkadaşları, bu özel olduklarını bile alanlara erişim böylece

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;
    };
};

Şimdi alanlar üzerinde yineleme için ziyaretçi desen kullanıyoruz. Alanların sayısı 0 dan MPL bir dizi yaratıyoruz ve o dizinde alan veri erişim. Alan veri kullanıcı tarafından sağlanan ziyaretçi: geçirir

struct field_visitor
{
    template<class C, class Visitor, class T>
    void operator()(C& c, Visitor v, T)
    {
        v(reflector::get_field_data<T::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));
}

Kader anı için şimdi hepsini bir araya koyduk. Person sınıf tanımlayabiliriz.

struct Person
{
    Person(const char *name, int age)
        :
        name(name),
        age(age)
    {
    }
private:
    REFLECTABLE
    (
        (const char *) name,
        (int) age
    )
};

Burada print_fields genelleştirilmiş fonksiyonu da var

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());
}

Bir örnek:

int main()
{
    Person p("Tom", 82);
    print_fields(p);
    return 0;
}

Çıkışları:

name=Tom
age=82

Ve voila, sadece C yansıma hayata geçirdik kod altında 100 satır.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Jonathan Leack

    Jonathan Lea

    26 ŞUBAT 2007
  • TechnoBuffalo

    TechnoBuffal

    8 HAZİRAN 2007
  • Utah Valley Online

    Utah Valley

    9 AĞUSTOS 2010