Bagaimana saya bisa menambahkan refleksi ke aplikasi C ++?

263

Saya ingin dapat mengintrospeksi kelas C ++ untuk nama, isinya (yaitu anggota dan tipenya) dll. Saya berbicara asli C ++ di sini, tidak dikelola C ++, yang memiliki refleksi. Saya menyadari C ++ menyediakan beberapa informasi terbatas menggunakan RTTI. Perpustakaan tambahan apa (atau teknik lain) yang bisa memberikan informasi ini?

Nick
sumber
18
Beruntung, Anda tidak dapat melakukannya tanpa makro dan preprocessing lainnya, karena metadata yang diperlukan tidak ada kecuali jika Anda membuatnya secara manual melalui beberapa sihir preprocessing makro.
jalf
6
Informasi yang Anda dapat kembali dari RTTI tidak cukup untuk melakukan sebagian besar hal yang sebenarnya ingin Anda renungkan. Misalnya, Anda tidak dapat mengulangi fungsi anggota dari suatu kelas.
Joseph Garvin

Jawaban:

259

Yang perlu Anda lakukan adalah memiliki preprocessor menghasilkan data refleksi tentang bidang. Data ini dapat disimpan sebagai kelas bersarang.

Pertama, untuk membuatnya lebih mudah dan lebih bersih untuk menulisnya di preprocessor kita akan menggunakan ekspresi yang diketik. Ekspresi yang diketik hanyalah ekspresi yang menempatkan jenis dalam tanda kurung. Jadi, alih-alih menulis, int xAnda akan menulis (int) x. Berikut adalah beberapa makro berguna untuk membantu dengan ekspresi yang diketik:

#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

Selanjutnya, kami mendefinisikan REFLECTABLEmakro untuk menghasilkan data tentang setiap bidang (ditambah bidang itu sendiri). Makro ini akan disebut seperti ini:

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

Jadi menggunakan Boost.PP kita beralih pada setiap argumen dan menghasilkan data seperti ini:

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

Apa yang dilakukan adalah menghasilkan konstanta fields_nyang merupakan jumlah bidang yang dapat dipantulkan di kelas. Maka itu spesialisasi field_datauntuk setiap bidang. Ini juga teman reflectorkelas, ini sehingga dapat mengakses bidang bahkan ketika mereka pribadi:

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

Sekarang untuk beralih ke bidang yang kita gunakan pola pengunjung. Kami membuat rentang MPL dari 0 hingga jumlah bidang, dan mengakses data bidang pada indeks itu. Kemudian data lapangan diteruskan ke pengunjung yang disediakan pengguna:

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

Sekarang untuk momen kebenaran, kami menggabungkan semuanya. Inilah cara kita mendefinisikan Personkelas yang dapat dipantulkan:

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

Berikut adalah print_fieldsfungsi umum yang menggunakan data refleksi untuk beralih di bidang:

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

Contoh menggunakan print_fieldsdengan Personkelas yang dapat dipantulkan :

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

Output yang mana:

name=Tom
age=82

Dan voila, kami baru saja mengimplementasikan refleksi dalam C ++, di bawah 100 baris kode.

Paul Fultz II
sumber
106
Kudos karena menunjukkan bagaimana menerapkan refleksi, daripada mengatakan itu tidak bisa dilakukan. Jawaban seperti ini yang menjadikan SO sumber yang bagus.
fearless_fool
4
Perhatikan bahwa jika Anda mencoba untuk mengkompilasi ini di bawah Visual Studio Anda akan mendapatkan kesalahan karena VS tidak menangani ekspansi makro variadic dengan benar. Untuk VS, coba tambahkan: #define DETAIL_TYPEOF_INT2(tuple) DETAIL_TYPEOF_HEAD tupledan #define DETAIL_TYPEOF_INT(...) DETAIL_TYPEOF_INT2((__VA_ARGS__)) dan ubah definisi TYPEOF (x) ke:#define TYPEOF(x) DETAIL_TYPEOF_INT(DETAIL_TYPEOF_PROBE x,)
Phenglei Kai
Saya mendapatkan kesalahan 'BOOST_PP_IIF_0' tidak menyebutkan jenis. Tolong bantu.
Ankit Zalani
3
Lihat jawaban saya sendiri - stackoverflow.com/a/28399807/2338477 Saya telah mengekstraksi dan mengemas semua definisi, dan meningkatkan perpustakaan tidak diperlukan. Sebagai kode demo, saya memberikan serialisasi ke xml dan memulihkan dari xml.
TarmoPikaro
106

Ada dua macam reflectionberenang di sekitar.

  1. Inspeksi dengan mengulangi anggota sejenis, menyebutkan metode dan sebagainya.

    Ini tidak mungkin dengan C ++.
  2. Inspeksi dengan memeriksa apakah tipe kelas (kelas, struct, union) memiliki metode atau tipe bersarang, berasal dari tipe tertentu lainnya.

    Hal semacam ini dimungkinkan dengan menggunakan C ++ template-tricks. Menggunakanboost::type_traits untuk banyak hal (seperti memeriksa apakah suatu tipe integral). Untuk memeriksa keberadaan fungsi anggota, gunakan Apakah mungkin untuk menulis templat untuk memeriksa keberadaan fungsi? . Untuk memeriksa apakah ada tipe bersarang tertentu, gunakan SFINAE biasa .

Jika Anda mencari cara untuk mencapai 1), seperti melihat berapa banyak metode yang dimiliki kelas, atau seperti mendapatkan representasi string dari id kelas, maka saya khawatir tidak ada cara Standar C ++ untuk melakukan ini. Anda harus menggunakan keduanya

  • Meta Compiler seperti Qt Meta Object Compiler yang menerjemahkan kode Anda menambahkan informasi meta tambahan.
  • Kerangka kerja yang membatasi makro yang memungkinkan Anda untuk menambahkan informasi meta yang diperlukan. Anda perlu memberi tahu kerangka kerja semua metode, nama-nama kelas, kelas-dasar dan semua yang dibutuhkannya.

C ++ dibuat dengan kecepatan dalam pikiran. Jika Anda ingin inspeksi tingkat tinggi, seperti C # atau Java, maka saya khawatir saya harus memberi tahu Anda tidak ada cara tanpa usaha.

Johannes Schaub - litb
sumber
122
C ++ dibuat dengan kecepatan dalam pikiran, tetapi filosofi itu tidak "secepat mungkin," melainkan, "Anda tidak membayar untuk itu jika Anda tidak menggunakannya." Saya percaya mungkin bagi sebuah bahasa untuk mengimplementasikan introspeksi dengan cara yang sesuai dengan filosofi itu, C ++ justru kurang.
Joseph Garvin
8
@ Joseph: Bagaimana seharusnya itu dilakukan? Semua metadata itu harus disimpan. Yang berarti Anda harus membayar untuk itu, bahkan jika Anda tidak menggunakannya. (Kecuali jika Anda dapat menandai masing-masing tipe sebagai "pendukung refleksi", tapi kemudian kami hampir turun di mana kami mungkin juga menggunakan tipu daya makro yang ada.
jalf
25
@jalf: Hanya metadata yang mungkin diperlukan. Jika kita hanya mempertimbangkan refleksi waktu kompilasi, ini sepele. Misalnya fungsi waktu kompilasi members<T>yang mengembalikan daftar semua anggota T. Jika kita ingin memiliki refleksi runtime (yaitu RTTI dicampur dengan refleksi), kompiler masih akan tahu semua tipe dasar yang dipantulkan. Kemungkinan besar members<T>(T&)tidak akan pernah dipakai untuk T = std :: string, jadi RTTI untuk std :: string atau kelas turunannya tidak perlu dimasukkan.
MSalters
9
Perpustakaan refleks (disebutkan di bawah) menambahkan refleksi ke C ++ tanpa memperlambat kode yang ada di: root.cern.ch/drupal/content/reflex
Joseph Lisee
6
@ Jo: Refleksi tidak pernah memperlambat kode yang ada. Itu hanya membuat hal-hal yang dikirim lebih besar (karena Anda harus memberikan basis data info type ...).
mmmmmmmm
56

Dan saya akan suka kuda poni, tetapi kuda poni tidak gratis. :-p

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI adalah apa yang akan Anda dapatkan. Refleksi seperti yang Anda pikirkan - metadata deskriptif penuh tersedia saat runtime - tidak ada untuk C ++ secara default.

Brad Wilson
sumber
1
Brad kedua. Templat C ++ dapat menjadi lebih kuat, dan ada banyak pengalaman seputar berbagai perilaku tipe 'refleksi', seperti meningkatkan pustaka 'any', tipe sifat, C ++ RTTI dll. Yang dapat menyelesaikan banyak masalah yang dipecahkan oleh refleksi. Jadi Nick, apa tujuanmu di sini?
Aaron
7
Suara positif untuk komentar kuda! Saya akan memilih dua kali, karena jawaban Anda juga layak mendapatkannya, tetapi sayangnya saya hanya mendapatkan satu, jadi kuda poni menang. :-)
Franci Penov
6
Saya tidak benar-benar mengerti mengapa ini adalah respons yang cerdas. Saya sudah mengatakan saya ingin referensi ke perpustakaan dll untuk mengimplementasikan ini. Refleksi / introspeksi adalah untuk berbagai sistem untuk memungkinkan akses skrip, serialisasi dll.
Nick
3
@Nick: Dia sudah menjawab itu. Itu tidak dapat dilakukan, datanya tidak ada, dan oleh karena itu, tidak ada perpustakaan yang dapat mengimplementasikannya untuk Anda.
jalf
@jalf Masih aneh bagi saya membaca orang-orang di dunia pemrograman mengatakan berpikir seperti 'tidak mungkin' dan bukan 'Saya tidak tahu caranya'. Tentu metadata tidak ada tetapi dapat dimasukkan dengan makro
Freddx L.
38

Informasi itu ada - tetapi tidak dalam format yang Anda butuhkan, dan hanya jika Anda mengekspor kelas Anda. Ini berfungsi di Windows, saya tidak tahu tentang platform lain. Menggunakan specifier kelas penyimpanan seperti pada, misalnya:

class __declspec(export) MyClass
{
public:
    void Foo(float x);
}

Ini membuat kompiler membuat data definisi kelas ke dalam DLL / Exe. Tapi itu tidak dalam format yang dapat Anda gunakan untuk refleksi.

Di perusahaan saya, kami membangun perpustakaan yang menginterpretasikan metadata ini, dan memungkinkan Anda untuk merefleksikan kelas tanpa memasukkan makro dll, ke dalam kelas itu sendiri. Ini memungkinkan fungsi dipanggil sebagai berikut:

MyClass *instance_ptr=new MyClass;
GetClass("MyClass")->GetFunction("Foo")->Invoke(instance_ptr,1.331);

Ini secara efektif tidak:

instance_ptr->Foo(1.331);

Fungsi Invoke (this_pointer, ...) memiliki argumen variabel. Tentunya dengan memanggil fungsi dengan cara ini Anda menghindari hal-hal seperti const-safety dan sebagainya, sehingga aspek-aspek ini diimplementasikan sebagai pemeriksaan runtime.

Saya yakin sintaksnya dapat ditingkatkan, dan ini hanya berfungsi pada Win32 dan Win64 sejauh ini. Kami merasa sangat berguna untuk memiliki antarmuka GUI otomatis ke kelas, membuat properti di C ++, streaming ke dan dari XML dan sebagainya, dan tidak perlu diturunkan dari kelas dasar tertentu. Jika ada permintaan yang cukup mungkin kita bisa mengetuknya agar rilis.

Roderick
sumber
1
Saya pikir maksud Anda __declspec(dllexport)dan Anda dapat mengambil informasi dari file .map jika Anda mengaktifkan pembuatannya selama membangun.
Orwellophile
18

Refleksi tidak didukung oleh C ++ di luar kotak. Ini menyedihkan karena membuat tes pertahanan menjadi sakit.

Ada beberapa pendekatan untuk melakukan refleksi:

  1. gunakan informasi debug (non portable).
  2. Taburkan kode Anda dengan makro / templat atau pendekatan sumber lain (terlihat jelek)
  3. Ubah kompiler seperti dentang / gcc untuk menghasilkan database.
  4. Gunakan pendekatan Qt moc
  5. Tingkatkan Refleksi
  6. Refleksi Yang Tepat dan Rata

Tautan pertama terlihat paling menjanjikan (menggunakan mod untuk berdering), yang kedua membahas sejumlah teknik, yang ketiga adalah pendekatan yang berbeda menggunakan gcc:

  1. http://www.donw.org/rfl/

  2. https://bitbucket.org/dwilliamson/clreflect

  3. https://root.cern.ch/how/how-use-reflex

Sekarang ada kelompok kerja untuk refleksi C ++. Lihat berita untuk C ++ 14 @ CERN:

Edit 13/08/17:

Sejak posting asli telah ada sejumlah kemajuan potensial pada refleksi. Berikut ini memberikan lebih banyak detail dan diskusi tentang berbagai teknik dan status:

  1. Refleksi Statis Singkatnya
  2. Refleksi statis
  3. Desain untuk refleksi statis

Namun itu tidak terlihat menjanjikan pada pendekatan refleksi standar dalam C ++ dalam waktu dekat kecuali ada banyak minat dari masyarakat dalam mendukung refleksi dalam C ++.

Berikut ini rincian status saat ini berdasarkan umpan balik dari pertemuan standar C ++ terakhir:

Edit 13/12/2017

Refleksi terlihat bergerak menuju C ++ 20 atau lebih mungkin TSR. Namun gerakannya lambat.

Edit 15/09/2018

Draf TS telah dikirim ke badan nasional untuk pemungutan suara.

Teks dapat ditemukan di sini: https://github.com/cplusplus/reflection-ts

Edit 11/07/2019

TS refleksi adalah fitur lengkap dan keluar untuk komentar dan memilih selama musim panas (2019).

Pendekatan pemrograman meta-template harus diganti dengan pendekatan kode waktu kompilasi yang lebih sederhana (tidak tercermin dalam TS).

Edit 10/02/2020

Ada permintaan untuk mendukung TS refleksi di Visual Studio di sini:

Bicara di TS oleh penulis David Sankel:

Sunting 17 Maret 2020

Kemajuan refleksi sedang dibuat. Laporan dari '2020-02 Praha ISO C ++ Laporan Perjalanan Komite' dapat ditemukan di sini:

Detail tentang apa yang sedang dipertimbangkan untuk C ++ 23 dapat ditemukan di sini (termasuk bagian pendek tentang Refleksi):

Sunting 4 Juni 2020

Kerangka kerja baru telah dirilis oleh Jeff Preshing yang disebut 'Plywood' yang berisi mekanisme untuk refleksi runtime. Lebih detail dapat ditemukan di sini:

Alat dan pendekatannya terlihat paling halus dan paling mudah digunakan sejauh ini.

Damian Dixon
sumber
1
Tautan cern rusak.
Mostowski Tutup
tautan cern harus diperbaiki sekarang. Mereka cenderung sering patah yang merupakan rasa sakit.
Damian Dixon
Apakah jawaban ini hanya menganggap refleksi waktu kompilasi?
einpoklum
@einpoklum satu-satunya solusi saat ini untuk refleksi adalah waktu kompilasi, biasanya dengan kode meta-template atau makro. Draf TS terbaru sepertinya berfungsi untuk runtime tetapi Anda harus membangun semua perpustakaan dengan kompiler yang benar agar metadata yang diperlukan telah disimpan.
Damian Dixon
@DianDixon: Itu tidak benar. Ada beberapa perpustakaan refleksi run-time. Sekarang, memang, mereka agak kikuk dan baik memilih atau memerlukan nodifikasi kompilator, tetapi mereka masih ada. Jika, seperti yang saya mengerti komentar Anda, Anda hanya merujuk pada waktu kompilasi refleksi, silakan edit jawaban Anda untuk membuatnya lebih jelas.
einpoklum
15

Anda perlu melihat apa yang Anda coba lakukan, dan apakah RTTI akan memenuhi kebutuhan Anda. Saya telah menerapkan pseudo-refleksi saya sendiri untuk beberapa tujuan yang sangat spesifik. Sebagai contoh, saya pernah ingin dapat secara fleksibel mengkonfigurasi apa yang akan dihasilkan oleh simulasi. Diperlukan menambahkan beberapa kode boilerplate ke kelas yang akan di-output:

namespace {
  static bool b2 = Filter::Filterable<const MyObj>::Register("MyObject");
} 

bool MyObj::BuildMap()
{
  Filterable<const OutputDisease>::AddAccess("time", &MyObj::time);
  Filterable<const OutputDisease>::AddAccess("person", &MyObj::id);
  return true;
}

Panggilan pertama menambahkan objek ini ke sistem penyaringan, yang memanggil BuildMap()metode untuk mencari tahu metode apa yang tersedia.

Kemudian, dalam file konfigurasi, Anda dapat melakukan sesuatu seperti ini:

FILTER-OUTPUT-OBJECT   MyObject
FILTER-OUTPUT-FILENAME file.txt
FILTER-CLAUSE-1        person == 1773
FILTER-CLAUSE-2        time > 2000

Melalui beberapa templat sulap yang melibatkan boost, ini akan diterjemahkan ke dalam serangkaian panggilan metode pada saat run-time (ketika file konfigurasi dibaca), sehingga cukup efisien. Saya tidak akan merekomendasikan melakukan ini kecuali Anda benar-benar perlu, tetapi, ketika Anda melakukannya, Anda dapat melakukan beberapa hal yang sangat keren.

KeithB
sumber
Harus suka fungsi-fungsi ini yang selalu kembali benar;) Saya menganggap ini kebal dari masalah pemesanan init statis?
paulm
14

Saya akan merekomendasikan menggunakan Qt .

Ada lisensi sumber terbuka serta lisensi komersial.

Jérôme
sumber
1
Saya melihat ini tetapi menggunakan makro dan kode sumber perlu parsing untuk menghasilkan kode meta-data. Saya ingin menghindari langkah ekstra ini. Saya lebih suka menggunakan perpustakaan C ++ atau makro sederhana. Terima kasih untuk idenya.
Nick
10
QT, atau perpustakaan lain yang menerapkan pendekatan serupa adalah yang terbaik yang akan Anda dapatkan
jalf
5
Bayar pada waktu kompilasi atau bayar saat runtime - baik cara Anda membayar!
Martin Beckett
13

Apa yang Anda coba lakukan dengan refleksi?
Anda dapat menggunakan sifat tipe Boost dan tipe perpustakaan sebagai bentuk refleksi kompilasi waktu terbatas. Artinya, Anda dapat memeriksa dan memodifikasi properti dasar dari jenis yang diteruskan ke templat.

Ferruccio
sumber
13

EDIT : CAMP tidak lagi dipertahankan; dua garpu tersedia:

  • Satu juga disebut CAMP , dan didasarkan pada API yang sama.
  • Renungan adalah penulisan ulang sebagian, dan akan dipilih karena tidak memerlukan Peningkatan; itu menggunakan C ++ 11.

CAMP adalah perpustakaan berlisensi MIT (sebelumnya LGPL) yang menambahkan refleksi ke bahasa C ++. Itu tidak memerlukan langkah preprocessing spesifik dalam kompilasi, tetapi mengikat harus dilakukan secara manual.

Pustaka Tegesoft saat ini menggunakan Boost, tetapi ada juga garpu menggunakan C ++ 11 yang tidak lagi membutuhkan Boost .

filant
sumber
11

Saya melakukan sesuatu seperti apa yang Anda cari sekali, dan sementara itu mungkin untuk mendapatkan beberapa tingkat refleksi dan akses ke fitur tingkat yang lebih tinggi, sakit kepala pemeliharaan mungkin tidak sepadan. Sistem saya digunakan untuk menjaga kelas UI sepenuhnya terpisah dari logika bisnis melalui pendelegasian yang mirip dengan konsep Objective-C tentang penerusan dan penerusan pesan. Cara untuk melakukannya adalah dengan membuat beberapa kelas dasar yang mampu memetakan simbol (saya menggunakan string pool tetapi Anda bisa melakukannya dengan enum jika Anda lebih suka penanganan kesalahan waktu dan kompilasi-waktu atas fleksibilitas total) ke pointer fungsi (sebenarnya tidak pointer fungsi murni, tetapi sesuatu yang mirip dengan apa yang Boost miliki dengan Boost.Fungsi - yang saya tidak punya akses pada saat itu). Anda dapat melakukan hal yang sama untuk variabel anggota Anda selama Anda memiliki beberapa kelas dasar umum yang mampu mewakili nilai apa pun. Seluruh sistem adalah ripoff Key-Value Coding dan Delegation yang tidak malu-malu, dengan beberapa efek samping yang mungkin sepadan dengan jumlah waktu yang diperlukan untuk membuat setiap kelas yang menggunakan sistem untuk mencocokkan semua metode dan anggota dengan panggilan hukum : 1) Setiap kelas dapat memanggil metode apa pun pada kelas lain tanpa harus menyertakan header atau menulis kelas dasar palsu sehingga antarmuka dapat ditentukan sebelumnya untuk kompiler; dan 2) Getter dan setter dari variabel anggota mudah untuk membuat thread-safe karena mengubah atau mengakses nilai-nilai mereka selalu dilakukan melalui 2 metode di kelas dasar semua objek. Seluruh sistem adalah ripoff Key-Value Coding dan Delegation yang tidak malu-malu, dengan beberapa efek samping yang mungkin sepadan dengan jumlah waktu yang diperlukan untuk membuat setiap kelas yang menggunakan sistem untuk mencocokkan semua metode dan anggota dengan panggilan hukum : 1) Setiap kelas dapat memanggil metode apa pun pada kelas lain tanpa harus menyertakan header atau menulis kelas dasar palsu sehingga antarmuka dapat ditentukan sebelumnya untuk kompiler; dan 2) Getter dan setter dari variabel anggota mudah untuk membuat thread-safe karena mengubah atau mengakses nilai-nilai mereka selalu dilakukan melalui 2 metode di kelas dasar semua objek. Seluruh sistem adalah ripoff Key-Value Coding dan Delegation yang tidak malu-malu, dengan beberapa efek samping yang mungkin sepadan dengan jumlah waktu yang diperlukan untuk membuat setiap kelas yang menggunakan sistem untuk mencocokkan semua metode dan anggota dengan panggilan hukum : 1) Setiap kelas dapat memanggil metode apa pun pada kelas lain tanpa harus menyertakan header atau menulis kelas dasar palsu sehingga antarmuka dapat ditentukan sebelumnya untuk kompiler; dan 2) Getter dan setter dari variabel anggota mudah untuk membuat thread-safe karena mengubah atau mengakses nilai-nilai mereka selalu dilakukan melalui 2 metode di kelas dasar semua objek. 1) Setiap kelas dapat memanggil metode apa pun pada kelas lain tanpa harus menyertakan header atau menulis kelas dasar palsu sehingga antarmuka dapat ditentukan sebelumnya untuk kompiler; dan 2) Getter dan setter dari variabel anggota mudah untuk membuat thread-safe karena mengubah atau mengakses nilai-nilai mereka selalu dilakukan melalui 2 metode di kelas dasar semua objek. 1) Setiap kelas dapat memanggil metode apa pun pada kelas lain tanpa harus menyertakan header atau menulis kelas dasar palsu sehingga antarmuka dapat ditentukan sebelumnya untuk kompiler; dan 2) Getter dan setter dari variabel anggota mudah untuk membuat thread-safe karena mengubah atau mengakses nilai-nilai mereka selalu dilakukan melalui 2 metode di kelas dasar semua objek.

Ini juga mengarah pada kemungkinan melakukan beberapa hal yang sangat aneh yang sebaliknya tidak mudah di C ++. Misalnya saya bisa membuat objek Array yang berisi item sembarang jenis apa pun, termasuk dirinya sendiri, dan membuat array baru secara dinamis dengan mengirimkan pesan ke semua item array dan mengumpulkan nilai kembali (mirip dengan peta di Lisp). Yang lain adalah implementasi mengamati nilai-kunci, di mana saya bisa mengatur UI untuk segera menanggapi perubahan dalam anggota kelas backend bukannya terus-menerus mengumpulkan data atau menggambar ulang tampilan yang tidak perlu.

Mungkin yang lebih menarik bagi Anda adalah kenyataan bahwa Anda juga dapat membuang semua metode dan anggota yang ditentukan untuk kelas, dan dalam bentuk string tidak kurang.

Kerugian pada sistem yang mungkin membuat Anda tidak ingin repot: menambahkan semua pesan dan nilai kunci sangat membosankan; lebih lambat daripada tanpa refleksi; Anda akan tumbuh membenci melihat boost::static_pointer_castdanboost::dynamic_pointer_cast seluruh basis kode Anda dengan hasrat keras; keterbatasan sistem yang sangat diketik masih ada, Anda benar-benar hanya menyembunyikannya sedikit sehingga tidak sejelas itu. Kesalahan ketik dalam string Anda juga bukan kejutan yang menyenangkan atau mudah ditemukan.

Mengenai cara mengimplementasikan sesuatu seperti ini: cukup gunakan pointer yang dibagi dan lemah ke beberapa basis umum (milik saya sangat imajinatif disebut "Objek") dan turunkan untuk semua jenis yang ingin Anda gunakan. Saya akan merekomendasikan menginstal Boost.Fungsi daripada melakukannya dengan cara yang saya lakukan, yang dengan beberapa omong kosong kustom dan satu ton makro jelek untuk membungkus panggilan fungsi pointer. Karena semuanya dipetakan, menginspeksi objek hanyalah masalah iterasi melalui semua kunci. Karena kelas saya pada dasarnya sedekat dengan ripoff langsung Cocoa mungkin hanya menggunakan C ++, jika Anda menginginkan sesuatu seperti itu maka saya sarankan menggunakan dokumentasi Cocoa sebagai cetak biru.

Michel
sumber
Hai, @Michael; apakah Anda masih memiliki kode sumber untuk ini, atau apakah Anda membuangnya? Saya ingin melihatnya jika Anda tidak keberatan.
RandomDSdevel
Ups, salah mengeja nama Anda! Tidak heran saya tidak pernah mendapat balasan ...
RandomDSdevel
10

Ada perpustakaan baru untuk refleksi di C ++, yang disebut RTTR (Run Time Type Reflection, lihat juga github ).

Antarmuka mirip dengan refleksi dalam C # dan berfungsi tanpa RTTI.

Zack
sumber
8

Dua solusi seperti refleksi yang saya ketahui dari hari C ++ saya adalah:

1) Gunakan RTTI, yang akan menyediakan bootstrap bagi Anda untuk membangun perilaku seperti refleksi Anda, jika Anda bisa membuat semua kelas Anda berasal dari kelas dasar 'objek'. Kelas itu dapat memberikan beberapa metode seperti GetMethod, GetBaseClass dll. Adapun cara kerja metode-metode tersebut Anda perlu menambahkan beberapa makro secara manual untuk menghias tipe Anda, yang di belakang layar membuat metadata dalam tipe tersebut untuk memberikan jawaban kepada GetMethods dll.

2) Pilihan lain, jika Anda memiliki akses ke objek kompiler adalah menggunakan DIA SDK . Jika saya ingat dengan benar, ini memungkinkan Anda membuka pdb, yang seharusnya berisi metadata untuk tipe C ++ Anda. Mungkin cukup untuk melakukan apa yang Anda butuhkan. Halaman ini menunjukkan bagaimana Anda bisa mendapatkan semua tipe dasar kelas misalnya.

Kedua solusi ini agak jelek! Tidak ada yang seperti sedikit C ++ untuk membuat Anda menghargai kemewahan C #.

Semoga berhasil.

pengguna4385
sumber
Itu licik dan retas raksasa, dengan hal DIA SDK yang Anda sarankan di sana.
Sqeaky
7

EDIT: Tautan rusak yang diperbarui pada tanggal 7 Februari 2017.

Saya pikir tidak ada yang menyebutkan ini:

Di CERN mereka menggunakan sistem refleksi penuh untuk C ++:

Refleks CERN . Tampaknya bekerja dengan sangat baik.

Germán Diago
sumber
@ j4nbur53 Tautan ini rusak karena sepertinya mereka mencapai tonggak sejarah: root.cern.ch
Germán Diago
Mungkinkah maksud Anda tautan ini root.cern.ch/root/doc/ROOTUsersGuideHTML/ch07.html Bab Reflex?
Mostowski Collapse
Coba root.cern.ch/how/how-use-reflex ini . Refleks berfungsi sebagai generator yang mem-parsing file header Anda dan menghasilkan c ++ kode introspeksi / pustaka, yang dapat Anda tautkan dan gunakan api sederhana.
Adam Ryczkowski
6

Pertanyaan ini agak lama sekarang (tidak tahu mengapa saya terus memukul pertanyaan lama hari ini) tetapi saya sedang memikirkan BOOST_FUSION_ADAPT_STRUCT yang memperkenalkan refleksi waktu kompilasi.

Terserah kepada Anda untuk memetakan ini ke run-time reflection tentu saja, dan itu tidak akan terlalu mudah, tetapi mungkin dalam arah ini, sementara itu tidak akan terbalik :)

Saya benar-benar berpikir makro untuk merangkum yang BOOST_FUSION_ADAPT_STRUCTbisa menghasilkan metode yang diperlukan untuk mendapatkan perilaku runtime.

Matthieu M.
sumber
2
oleh minghua (yang awalnya mengedit pos): Saya menggali solusi BOOST_FUSION_ADAPT_STRUCT ini dan akhirnya menghasilkan sebuah contoh. Lihat pertanyaan SO yang lebih baru ini - C ++ beralih ke bidang struct bersarang dengan boost fusion adapt_struct .
Matthieu M.
Bagus, Matthieu! Baru sadar telah melihat petunjuk Anda di sana-sini selama setahun terakhir. Tidak memperhatikan mereka terkait sampai sekarang. Itu sangat menginspirasi.
minghua
6

Saya pikir Anda mungkin menemukan artikel yang menarik "Menggunakan Template untuk Refleksi dalam C ++" oleh Dominic Filion. Itu ada di bagian 1.4 dari Pemrograman Game Permata 5 . Sayangnya saya tidak membawa salinan saya, tetapi mencarinya karena saya pikir itu menjelaskan apa yang Anda minta.

Luis
sumber
4

Merenungkan adalah perpustakaan refleksi C ++, dalam menjawab pertanyaan ini. Saya mempertimbangkan opsi dan memutuskan untuk membuat pilihan saya sendiri karena saya tidak dapat menemukan yang mencentang semua kotak saya.

Meskipun ada jawaban bagus untuk pertanyaan ini, saya tidak ingin menggunakan banyak makro, atau mengandalkan Boost. Boost adalah pustaka yang hebat, tetapi ada banyak proyek kecil C ++ 0 dipesan lebih dahulu yang lebih sederhana dan memiliki waktu kompilasi yang lebih cepat. Ada juga keuntungan untuk dapat mendekorasi kelas secara eksternal, seperti membungkus pustaka C ++ yang tidak (belum?) Mendukung C ++ 11. Ini adalah fork dari CAMP, menggunakan C ++ 11, yang tidak lagi membutuhkan Boost .

Nick
sumber
4

Refleksi pada dasarnya adalah tentang apa yang diputuskan oleh kompiler untuk meninggalkan jejak kaki dalam kode yang dapat ditanyakan oleh kode runtime. C ++ terkenal karena tidak membayar untuk apa yang tidak Anda gunakan; karena kebanyakan orang tidak menggunakan / menginginkan refleksi, kompiler C ++ menghindari biaya dengan tidak merekam apa pun .

Jadi, C ++ tidak memberikan refleksi, dan tidak mudah untuk "mensimulasikan" sendiri sebagai aturan umum seperti yang dicatat oleh jawaban lain.

Di bawah "teknik lain", jika Anda tidak memiliki bahasa dengan refleksi, dapatkan alat yang dapat mengekstrak informasi yang Anda inginkan pada waktu kompilasi.

Perangkat Rekayasa Ulang Perangkat Lunak DMS Kami adalah teknologi kompiler umum yang diparameterisasi dengan definisi langauge eksplisit. Ini memiliki definisi langauge untuk C, C ++, Java, COBOL, PHP, ...

Untuk versi C, C ++, Java dan COBOL, ia menyediakan akses lengkap ke pohon parse, dan informasi tabel simbol. Informasi tabel simbol itu termasuk jenis data yang mungkin Anda inginkan dari "refleksi". Jika tujuan Anda adalah untuk menghitung beberapa set bidang atau metode dan melakukan sesuatu dengannya, DMS dapat digunakan untuk mengubah kode sesuai dengan apa yang Anda temukan di tabel simbol dengan cara sewenang-wenang.

Ira Baxter
sumber
3

Anda dapat menemukan perpustakaan lain di sini: http://www.garret.ru/cppreflection/docs/reflect.html Mendukung 2 cara: mendapatkan informasi jenis dari informasi debug dan membiarkan programmer untuk memberikan informasi ini.

Saya juga tertarik pada refleksi untuk proyek saya dan menemukan perpustakaan ini, saya belum mencobanya, tetapi mencoba alat-alat lain dari orang ini dan saya suka cara kerjanya :-)

alariq
sumber
3

Lihat Classdesc http://classdesc.sf.net . Ini memberikan refleksi dalam bentuk "deskriptor" kelas, bekerja dengan kompiler C ++ standar apa pun (ya diketahui bekerja dengan Visual Studio dan juga GCC), dan tidak memerlukan anotasi kode sumber (meskipun beberapa pragma ada untuk menangani situasi sulit ). Ini telah dikembangkan selama lebih dari satu dekade, dan digunakan dalam sejumlah proyek skala industri.

Russell Standish
sumber
1
Selamat datang di Stack Overflow. Meskipun jawaban ini ada pada topik, penting untuk menunjukkan bahwa Anda adalah pembuat perangkat lunak ini, untuk memperjelas bahwa ini bukan rekomendasi yang tidak bias :-)
Matthew Strawbridge
2

Ketika saya ingin refleksi dalam C ++ saya membaca artikel ini dan meningkatkan apa yang saya lihat di sana. Maaf, tidak bisa. Saya tidak memiliki hasilnya ... tetapi Anda pasti bisa mendapatkan apa yang saya miliki dan pergi dari sana.

Saat ini saya sedang meneliti, ketika saya merasa seperti itu, metode untuk menggunakan inherit_linearly untuk membuat definisi tipe yang mudah dipantulkan jauh lebih mudah. Sebenarnya saya sudah cukup jauh di dalamnya tetapi saya masih memiliki cara untuk pergi. Perubahan dalam C ++ 0x sangat mungkin banyak membantu di bidang ini.

Edward Strange
sumber
2

Sepertinya C ++ masih tidak memiliki fitur ini. Dan C ++ 11 refleksi tertunda juga ((

Cari beberapa makro atau buat sendiri. Qt juga dapat membantu refleksi (jika dapat digunakan).

Bohdan
sumber
2

meskipun refleksi tidak didukung di luar kotak di c ++, itu tidak terlalu sulit untuk diterapkan. Saya telah menemukan artikel yang bagus ini: http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

artikel ini menjelaskan dengan sangat rinci bagaimana Anda dapat menerapkan sistem refleksi yang sangat sederhana dan belum sempurna. diberikan itu bukan solusi yang paling sehat, dan ada tepi kasar yang tersisa untuk diselesaikan tetapi untuk kebutuhan saya itu sudah cukup.

intinya - refleksi dapat membayar jika dilakukan dengan benar, dan itu sepenuhnya layak di c ++.

Naore Azenkut
sumber
2

Saya ingin mengiklankan keberadaan perangkat introspeksi / refleksi otomatis "IDK". Ia menggunakan meta-compiler seperti Qt's dan menambahkan informasi meta langsung ke file objek. Itu diklaim mudah digunakan. Tidak ada ketergantungan eksternal. Bahkan memungkinkan Anda untuk secara otomatis mencerminkan std :: string dan kemudian menggunakannya dalam skrip. Silakan lihat IDK

Eugene G
sumber
2

Jika Anda mencari refleksi C ++ yang relatif sederhana - Saya telah mengumpulkan dari berbagai sumber makro / mendefinisikan, dan berkomentar bagaimana cara kerjanya. Anda dapat mengunduh file tajuk dari sini:

https://github.com/tapika/TestCppReflect/blob/master/MacroHelpers.h

set definisi, ditambah fungsionalitas di atasnya:

https://github.com/tapika/TestCppReflect/blob/master/CppReflect.h https://github.com/tapika/TestCppReflect/blob/master/CppReflect.cpp https://github.com/tapika/TestCppefef/ gumpalan / master / TypeTraits.h

Contoh aplikasi juga berada di repositori git, di sini: https://github.com/tapika/TestCppReflect/

Saya akan menyalinnya di sini dengan penjelasan:

#include "CppReflect.h"
using namespace std;


class Person
{
public:

    // Repack your code into REFLECTABLE macro, in (<C++ Type>) <Field name>
    // form , like this:

    REFLECTABLE( Person,
        (CString)   name,
        (int)       age,
...
    )
};

void main(void)
{
    Person p;
    p.name = L"Roger";
    p.age = 37;
...

    // And here you can convert your class contents into xml form:

    CStringW xml = ToXML( &p );
    CStringW errors;

    People ppl2;

    // And here you convert from xml back to class:

    FromXml( &ppl2, xml, errors );
    CStringA xml2 = ToXML( &ppl2 );
    printf( xml2 );

}

REFLECTABLEdefine menggunakan nama kelas + nama bidang dengan offsetof- untuk mengidentifikasi di mana tempat dalam memori bidang tertentu berada. Saya telah mencoba untuk mengambil .NET terminologi sejauh mungkin, tetapi C ++ dan C # berbeda, jadi bukan 1 banding 1. Seluruh model refleksi C ++ berada di dalam TypeInfodan FieldInfokelas.

Saya telah menggunakan pugi xml parser untuk mengambil kode demo ke xml dan mengembalikannya dari xml.

Jadi output yang dihasilkan oleh kode demo terlihat seperti ini:

<?xml version="1.0" encoding="utf-8"?>
<People groupName="Group1">
    <people>
        <Person name="Roger" age="37" />
        <Person name="Alice" age="27" />
        <Person name="Cindy" age="17" />
    </people>
</People>

Dimungkinkan juga untuk mengaktifkan dukungan kelas / struktur pihak ke-3 apa pun melalui kelas TypeTraits, dan spesifikasi templat parsial - untuk menentukan kelas TypeTraitsT Anda sendiri, dengan cara yang mirip dengan CString atau int - lihat contoh kode di

https://github.com/tapika/TestCppReflect/blob/master/TypeTraits.h#L195

Solusi ini berlaku untuk Windows / Visual studio. Dimungkinkan untuk porting ke OS / kompiler lain, tetapi belum melakukannya. (Tanyakan kepada saya jika Anda benar-benar menyukai solusi, saya mungkin dapat membantu Anda)

Solusi ini berlaku untuk serialisasi satu shot dari satu kelas dengan beberapa subclass.

Namun, jika Anda mencari mekanisme untuk membuat serial bagian-bagian kelas atau bahkan untuk mengontrol apa yang dihasilkan oleh fungsi refleksi, Anda dapat melihat solusi berikut:

https://github.com/tapika/cppscriptcore/tree/master/SolutionProjectModel

Informasi lebih rinci dapat ditemukan dari video youtube:

C ++ Refleksi Jenis Runtime https://youtu.be/TN8tJijkeFE

Saya mencoba menjelaskan sedikit lebih dalam tentang bagaimana c ++ refleksi akan bekerja.

Contoh kode akan terlihat seperti contoh ini:

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/testCppApp.cpp

c.General.IntDir = LR"(obj\$(ProjectName)_$(Configuration)_$(Platform)\)";
c.General.OutDir = LR"(bin\$(Configuration)_$(Platform)\)";
c.General.UseDebugLibraries = true;
c.General.LinkIncremental = true;
c.CCpp.Optimization = optimization_Disabled;
c.Linker.System.SubSystem = subsystem_Console;
c.Linker.Debugging.GenerateDebugInformation = debuginfo_true;

Tetapi setiap langkah di sini sebenarnya menghasilkan panggilan fungsi Menggunakan properti C ++ dengan __declspec(property(get =, put ... ) .

yang menerima informasi lengkap tentang Tipe Data C ++, nama properti C ++ dan pointer instance kelas, dalam bentuk path, dan berdasarkan informasi itu Anda dapat menghasilkan xml, json, atau bahkan membuat serialisasi yang satu melalui internet.

Contoh fungsi panggilan balik virtual tersebut dapat ditemukan di sini:

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/VCConfiguration.cpp

Lihat fungsi ReflectCopy, dan fungsi virtual::OnAfterSetProperty .

Tapi karena topiknya benar-benar canggih - saya sarankan untuk memeriksa melalui video terlebih dahulu.

Jika Anda memiliki beberapa ide perbaikan, jangan ragu untuk menghubungi saya.

TarmoPikaro
sumber
2

The Random Access Refleksi perpustakaan membuat cukup mudah dan refleksi intuitif - semua informasi bidang / jenis ini dirancang untuk baik tersedia di array atau merasa seperti akses array. Ini ditulis untuk C ++ 17 dan bekerja dengan Visual Studios, g ++, dan Dentang. Pustaka adalah hanya tajuk, artinya Anda hanya perlu menyalin "Reflect.h" ke proyek Anda untuk menggunakannya.

Struct atau kelas yang direfleksikan membutuhkan makro REFLECT, di mana Anda memberikan nama kelas yang Anda refleksi dan nama-nama bidang.

class FuelTank {
    public:
        float capacity;
        float currentLevel;
        float tickMarks[2];

    REFLECT(() FuelTank, () capacity, () currentLevel, () tickMarks)
};

Itu semua ada, tidak ada kode tambahan yang diperlukan untuk mengatur refleksi. Secara opsional Anda dapat menyediakan kacamata super (dalam tanda kurung argumen pertama) dan anotasi bidang (dalam tanda kurung yang mendahului bidang yang ingin Anda beri catatan) untuk dapat melintasi kacamata super atau menambahkan informasi waktu kompilasi tambahan ke bidang (seperti Json: :Mengabaikan).

Perulangan melalui bidang bisa sesederhana ...

for ( size_t i=0; i<FuelTank::Class::TotalFields; i++ )
    std::cout << FuelTank::Class::Fields[i].name << std::endl;

Anda dapat mengulang melalui instance objek untuk mengakses nilai bidang (yang dapat Anda baca atau modifikasi) dan informasi jenis bidang ...

FuelTank::Class::ForEachField(fuelTank, [&](auto & field, auto & value) {
    using Type = typename std::remove_reference<decltype(value)>::type;
    std::cout << TypeToStr<Type>() << " " << field.name << ": " << value << std::endl;
});

Sebuah JSON Perpustakaan dibangun di atas RandomAccessReflection yang otomatis mengidentifikasi representasi keluaran JSON tepat untuk membaca atau menulis, dan secara rekursif dapat melintasi setiap bidang tercermin, serta array dan kontainer STL.

struct MyOtherObject { int myOtherInt; REFLECT(() MyOtherObject, () myOtherInt) };
struct MyObject
{
    int myInt;
    std::string myString;
    MyOtherObject myOtherObject;
    std::vector<int> myIntCollection;

    REFLECT(() MyObject, () myInt, () myString, (Reflected) myOtherObject, () myIntCollection)
};

int main()
{
    MyObject myObject = {};
    std::cout << "Enter MyObject:" << std::endl;
    std::cin >> Json::in(myObject);
    std::cout << std::endl << std::endl << "You entered:" << std::endl;
    std::cout << Json::pretty(myObject);
}

Di atas bisa dijalankan seperti ...

Enter MyObject:
{
  "myInt": 1337, "myString": "stringy", "myIntCollection": [2,4,6],
  "myOtherObject": {
    "myOtherInt": 9001
  }
}


You entered:
{
  "myInt": 1337,
  "myString": "stringy",
  "myOtherObject": {
    "myOtherInt": 9001
  },
  "myIntCollection": [ 2, 4, 6 ]
}

Lihat juga...

jjf28
sumber
1

Refleksi dalam C ++ sangat berguna, dalam kasus di sana Anda perlu menjalankan beberapa metode untuk setiap anggota (Misalnya: serialisasi, hashing, bandingkan). Saya datang dengan solusi umum, dengan sintaks yang sangat sederhana:

struct S1
{
    ENUMERATE_MEMBERS(str,i);
    std::string str;
    int i;
};
struct S2
{
    ENUMERATE_MEMBERS(s1,i2);
    S1 s1;
    int i2;
};

Di mana ENUMERATE_MEMBERS adalah makro, yang dijelaskan nanti (PEMBARUAN):

Asumsikan kita telah mendefinisikan fungsi serialisasi untuk int dan std :: string seperti ini:

void EnumerateWith(BinaryWriter & writer, int val)
{
    //store integer
    writer.WriteBuffer(&val, sizeof(int));
}
void EnumerateWith(BinaryWriter & writer, std::string val)
{
    //store string
    writer.WriteBuffer(val.c_str(), val.size());
}

Dan kami memiliki fungsi generik di dekat "makro rahasia";)

template<typename TWriter, typename T>
auto EnumerateWith(TWriter && writer, T && val) -> is_enumerable_t<T>
{
    val.EnumerateWith(write); //method generated by ENUMERATE_MEMBERS macro
}

Sekarang kamu bisa menulis

S1 s1;
S2 s2;
//....
BinaryWriter writer("serialized.bin");

EnumerateWith(writer, s1); //this will call EnumerateWith for all members of S1
EnumerateWith(writer, s2); //this will call EnumerateWith for all members of S2 and S2::s1 (recursively)

Jadi dengan memiliki ENUMERATE_MEMBERS makro dalam definisi struct, Anda dapat membangun serialisasi, membandingkan, hashing, dan hal-hal lain tanpa menyentuh tipe asli, satu-satunya persyaratan adalah menerapkan metode "EnumerateWith" untuk setiap jenis, yang tidak dapat dihitung, per enumerator (seperti BinaryWriter) . Biasanya Anda harus menerapkan 10-20 jenis "sederhana" untuk mendukung jenis apa pun dalam proyek Anda.

Makro ini harus memiliki nol-overhead untuk pembuatan / penghancuran struct dalam run-time, dan kode T.EnumerateWith () harus dihasilkan sesuai permintaan, yang dapat dicapai dengan membuatnya fungsi templat-inline, jadi satu-satunya overhead di semua ceritanya adalah untuk menambahkan ENUMERATE_MEMBERS (m1, m2, m3 ...) ke setiap struct, sementara menerapkan metode spesifik per jenis anggota adalah suatu keharusan dalam solusi apa pun, jadi saya tidak menganggapnya sebagai overhead.

UPDATE: Ada implementasi makro ENUMERATE_MEMBERS yang sangat sederhana (namun bisa sedikit diperluas untuk mendukung warisan dari enumerable struct)

#define ENUMERATE_MEMBERS(...) \
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) const { EnumerateWithHelper(enumerator, __VA_ARGS__ ); }\
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) { EnumerateWithHelper(enumerator, __VA_ARGS__); }

// EnumerateWithHelper
template<typename TEnumerator, typename ...T> inline void EnumerateWithHelper(TEnumerator & enumerator, T &...v) 
{ 
    int x[] = { (EnumerateWith(enumerator, v), 1)... }; 
}

// Generic EnumerateWith
template<typename TEnumerator, typename T>
auto EnumerateWith(TEnumerator & enumerator, T & val) -> std::void_t<decltype(val.EnumerateWith(enumerator))>
{
    val.EnumerateWith(enumerator);
}

Dan Anda tidak perlu perpustakaan pihak ke-3 untuk 15 baris kode ini;)

jenkas
sumber
1

Anda dapat mencapai fitur refleksi statis yang keren untuk struct dengan BOOST_HANA_DEFINE_STRUCT dari Boost :: Hana library.
Hana cukup fleksibel, tidak hanya untuk penggunaan yang Anda pikirkan tetapi untuk banyak pemrograman metaplat.

nnolte
sumber
0

Jika Anda mendeklarasikan pointer ke fungsi seperti ini:

int (*func)(int a, int b);

Anda dapat menetapkan tempat di memori untuk fungsi seperti ini (membutuhkan libdldan dlopen)

#include <dlfcn.h>

int main(void)
{
    void *handle;
    char *func_name = "bla_bla_bla";
    handle = dlopen("foo.so", RTLD_LAZY);
    *(void **)(&func) = dlsym(handle, func_name);
    return func(1,2);
}

Untuk memuat simbol lokal menggunakan tipuan, Anda dapat menggunakan dlopenbinary panggilan ( argv[0]).

Satu-satunya persyaratan untuk ini (selain dlopen(), libdl, dan dlfcn.h) adalah mengetahui argumen dan jenis fungsi.

SS Anne
sumber