Jak mogę dodać odbicie do aplikacji C ++?

263

Chciałbym mieć możliwość introspekcji klasy C ++ pod kątem jej nazwy, zawartości (tj. Członków i ich typów) itp. Mówię tutaj o natywnym C ++, nie zarządzanym C ++, który ma odbicie. Zdaję sobie sprawę, że C ++ dostarcza pewne ograniczone informacje za pomocą RTTI. Jakie dodatkowe biblioteki (lub inne techniki) mogłyby dostarczyć te informacje?

Nacięcie
źródło
18
Pech, nie możesz tego zrobić bez makr i innego przetwarzania wstępnego, ponieważ wymagane metadane nie istnieją, chyba że utworzysz je ręcznie za pomocą magii przetwarzania wstępnego.
lipiec
6
Informacje, które możesz odzyskać z RTTI, nie wystarczą jednak do zrobienia większości rzeczy, dla których tak naprawdę chciałbyś się zastanowić. Nie można na przykład iterować funkcji członka klasy.
Joseph Garvin

Odpowiedzi:

259

To, co musisz zrobić, to, aby preprocesor wygenerował dane odbicia na temat pól. Te dane mogą być przechowywane jako zagnieżdżone klasy.

Po pierwsze, aby ułatwić i uprościć pisanie go w preprocesorze, użyjemy wyrażenia pisanego na maszynie. Wyrażenie na maszynie jest tylko wyrażeniem, które umieszcza typ w nawiasie. Zamiast pisać int x, napiszesz (int) x. Oto kilka przydatnych makr pomocnych przy wpisywaniu wyrażeń:

#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

Następnie definiujemy REFLECTABLEmakro do generowania danych o każdym polu (oraz o samym polu). To makro będzie się nazywać tak:

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

Tak więc za pomocą Boost.PP iterujemy każdy argument i generujemy dane w następujący sposób:

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

Powoduje to wygenerowanie stałej, fields_nktóra jest liczbą pól odblaskowych w klasie. Następnie specjalizuje się field_dataw każdej dziedzinie. Zaprzyjaźnia się także z reflectorklasą, dzięki czemu ma dostęp do pól, nawet gdy są prywatne:

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

Teraz do iteracji po polach używamy wzorca odwiedzającego. Tworzymy zakres MPL od 0 do liczby pól i uzyskujemy dostęp do danych pól pod tym indeksem. Następnie przekazuje dane pola odwiedzającemu użytkownika:

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

Teraz chwila prawdy zebraliśmy to wszystko. Oto, w jaki sposób możemy zdefiniować Personklasę, która jest odzwierciedlalna:

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

Oto uogólniona print_fieldsfunkcja wykorzystująca dane odbicia do iteracji po polach:

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

Przykład użycia print_fieldsz Personklasą odblaskową :

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

Które wyjścia:

name=Tom
age=82

I voila, właśnie wdrożyliśmy odbicie w C ++, w mniej niż 100 liniach kodu.

Paul Fultz II
źródło
106
Wyrazy uznania za pokazanie, jak wdrożyć refleksję, zamiast mówić, że nie da się tego zrobić. Takie odpowiedzi sprawiają, że SO jest świetnym źródłem.
nieustraszony_fool
4
Zauważ, że jeśli spróbujesz skompilować to w Visual Studio, pojawi się błąd, ponieważ VS nie obsługuje poprawnie rozwijania makr variadic. W przypadku VS spróbuj dodać: #define DETAIL_TYPEOF_INT2(tuple) DETAIL_TYPEOF_HEAD tuplei #define DETAIL_TYPEOF_INT(...) DETAIL_TYPEOF_INT2((__VA_ARGS__)) zmień definicję TYPEOF (x) na:#define TYPEOF(x) DETAIL_TYPEOF_INT(DETAIL_TYPEOF_PROBE x,)
Phenglei Kai
Pojawia się błąd „BOOST_PP_IIF_0” nie określa typu. Czy możesz mi pomóc?
Ankit Zalani,
3
Zobacz moją własną odpowiedź - stackoverflow.com/a/28399807/2338477 Wyodrębniłem i przepakowałem wszystkie definicje, a biblioteka rozszerzeń nie jest potrzebna. Jako kod demo udostępniam serializację do xml i przywracam z xml.
TarmoPikaro
106

Istnieją dwa rodzaje reflectionpływania.

  1. Inspekcja poprzez iterację elementów typu, wyliczenie metod i tak dalej.

    Nie jest to możliwe w C ++.
  2. Kontrola poprzez sprawdzenie, czy typ klasy (klasa, struktura, unia) ma metodę, czy typ zagnieżdżony, pochodzi od innego określonego typu.

    Tego rodzaju rzeczy są możliwe przy użyciu C ++ template-tricks. Użyj boost::type_traitsdo wielu rzeczy (takich jak sprawdzanie, czy typ jest całką). Aby sprawdzić istnienie funkcji składowej, użyj Czy można napisać szablon, aby sprawdzić istnienie funkcji? . Aby sprawdzić, czy istnieje pewien typ zagnieżdżony, użyj zwykłego SFINAE .

Jeśli szukasz sposobów na osiągnięcie 1), na przykład sprawdzania, ile metod ma klasa, lub uzyskania reprezentacji ciągu identyfikatora klasy, obawiam się, że nie ma na to sposobu w Standard C ++. Musisz użyć albo

  • Meta Compiler, taki jak Qt Meta Object Compiler, który tłumaczy kod, dodając dodatkowe informacje o meta.
  • Framework składający się z makr, które pozwalają dodawać wymagane meta-informacje. Będziesz musiał powiedzieć frameworkowi wszystkie metody, nazwy klas, klasy podstawowe i wszystko, czego potrzebuje.

C ++ jest stworzony z myślą o szybkości. Jeśli chcesz inspekcji na wysokim poziomie, takiej jak C # lub Java, obawiam się, że muszę ci powiedzieć, że nie ma drogi bez wysiłku.

Johannes Schaub - litb
źródło
122
C ++ jest tworzony z myślą o szybkości, ale filozofia nie jest „tak szybka, jak to możliwe”, lecz „nie płacisz za to, jeśli go nie używasz”. Wierzę, że język może wdrożyć introspekcję w sposób zgodny z tą filozofią, C ++ po prostu jej brakuje.
Joseph Garvin
8
@Joseph: Jak to zrobić? Wymagałoby to przechowywania wszystkich tych metadanych. Co oznacza, że ​​musisz za to zapłacić, nawet jeśli go nie używasz. (Chyba że można oznaczyć poszczególne typy jako „wspomagające odbicie”, ale jesteśmy prawie na dole, gdzie równie dobrze moglibyśmy skorzystać z istniejącej sztuczki makro.
09:00
25
@jalf: Tylko metadane, które mogą być potrzebne. Jeśli weźmiemy pod uwagę tylko odbicie w czasie kompilacji, jest to trywialne. Np. Funkcja czasu kompilacji, members<T>która zwraca listę wszystkich członków T. Gdybyśmy chcieli mieć odbicie środowiska uruchomieniowego (tj. RTTI zmieszane z odbiciem), kompilator nadal zna wszystkie odbite typy bazowe. Jest całkiem prawdopodobne, members<T>(T&)że nigdy nie zostanie utworzona instancja dla T = std :: string, więc RTTI dla std :: string lub jego pochodnych klas nie musi być uwzględnione.
MSalters
9
Biblioteka odruchów (wspomniana poniżej) dodaje odbicie do C ++ bez spowalniania istniejącego kodu pod adresem: root.cern.ch/drupal/content/reflex
Joseph Lisee
6
@Joe: Refleksja nigdy nie spowalnia istniejącego kodu. To po prostu powiększa dostarczane rzeczy (ponieważ musisz dostarczyć bazę danych informacji o typie ...).
mmmmmmmm
56

I chciałbym kucyka, ale kucyki nie są wolne. :-p

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI jest tym, co dostaniesz. Refleksja, o której myślisz - w pełni opisowe metadane dostępne w czasie wykonywania - po prostu domyślnie nie istnieje dla C ++.

Brad Wilson
źródło
1
Drugi Brad. Szablony C ++ mogą być dość wydajne i istnieje bogate doświadczenie wokół różnych zachowań typu „odbicie”, takich jak zwiększenie „dowolnej” biblioteki, cech typu, C ++ RTTI itp., Które mogą rozwiązać wiele problemów, dla których odbicie zostało rozwiązane. Nick, jaki jest twój cel tutaj?
Aaron
7
Uwaga na kucyki uwaga! Głosowałbym dwa razy, ponieważ twoja odpowiedź również na to zasługuje, ale niestety dostaję tylko jedną, więc kucyki wygrywają. :-)
Franci Penov
6
Naprawdę nie rozumiem, dlaczego jest to sprytna odpowiedź. Powiedziałem już, że chcę wprowadzić odniesienia do bibliotek itp., Aby to wdrożyć. Odbicie / introspekcja dotyczy różnych systemów, aby umożliwić dostęp do skryptu, serializacji itp.
Nick
3
@Nick: Już na to odpowiedział. Nie można tego zrobić, dane nie istnieją, dlatego żadna biblioteka nie jest w stanie zaimplementować ich za Ciebie.
czerwiec
@jalf Wciąż dziwne jest dla mnie czytanie ludzi w świecie programowania, którzy mówią: „to niemożliwe”, a nie „nie wiem jak”. Upewnij się, że metadane nie istnieją, ale można je wstawić za pomocą makr
Freddx L.,
38

Informacje istnieją - ale nie w wymaganym formacie i tylko jeśli wyeksportujesz swoje klasy. Działa to w systemie Windows, nie wiem o innych platformach. Używanie specyfikatorów klasy pamięci jak na przykład:

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

To powoduje, że kompilator buduje dane definicji klasy w DLL / Exe. Ale nie jest w formacie, którego można łatwo użyć do refleksji.

W mojej firmie zbudowaliśmy bibliotekę, która interpretuje te metadane i umożliwia odzwierciedlenie klasy bez wstawiania dodatkowych makr itp. W samej klasie. Pozwala na wywoływanie funkcji w następujący sposób:

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

To skutecznie:

instance_ptr->Foo(1.331);

Funkcja Invoke (this_pointer, ...) ma zmienne argumenty. Oczywiście przez wywołanie funkcji w ten sposób omijasz rzeczy takie jak const-safety itp., Więc te aspekty są implementowane jako kontrole środowiska wykonawczego.

Jestem pewien, że składnię można poprawić, a do tej pory działa ona tylko w systemach Win32 i Win64. Przekonaliśmy się, że jest to bardzo przydatne do posiadania automatycznych interfejsów GUI do klas, tworzenia właściwości w C ++, przesyłania strumieniowego do i z XML itd. I nie ma potrzeby czerpania z konkretnej klasy bazowej. Jeśli jest wystarczający popyt, może uda nam się go ukształtować do wydania.

Roderick
źródło
1
Myślę, że masz na myśli __declspec(dllexport)i możesz pobrać informacje z pliku .map, jeśli włączysz tworzenie takich plików podczas kompilacji.
Orwellophile,
18

Odbicie nie jest obsługiwane przez C ++ po wyjęciu z pudełka. Jest to smutne, ponieważ sprawia, że ​​próby obronne są uciążliwe.

Istnieje kilka podejść do refleksji:

  1. użyj informacji debugowania (nieprzenośnych).
  2. Posyp swój kod makro / szablonami lub innym podejściem źródłowym (wygląda brzydko)
  3. Zmodyfikuj kompilator, taki jak clang / gcc, aby utworzyć bazę danych.
  4. Użyj podejścia Qt moc
  5. Boost Reflect
  6. Precyzyjne i płaskie odbicie

Pierwszy link wygląda najbardziej obiecująco (używa modów do clang), drugi omawia szereg technik, trzeci to inne podejście przy użyciu gcc:

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

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

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

Istnieje teraz grupa robocza ds. Refleksji nad C ++. Zobacz wiadomości dla C ++ 14 @ CERN:

Edytuj 13/08/17:

Od czasu oryginalnego postu nastąpiła pewna liczba potencjalnych postępów w tej refleksji. Poniżej podano więcej szczegółów i omówienie różnych technik i statusu:

  1. Odbicie statyczne w pigułce
  2. Odbicie statyczne
  3. Projekt odbicia statycznego

Jednak nie wygląda obiecująco na znormalizowane podejście do refleksji w C ++ w najbliższej przyszłości, chyba że zainteresowanie społeczności wsparciem dla refleksji w C ++ będzie znacznie większe.

Poniżej przedstawiono szczegóły obecnego stanu na podstawie opinii z ostatniego spotkania standardów C ++:

Edytuj 13.12.2017

Wygląda na to, że refleksja zmierza w kierunku C ++ 20 lub więcej prawdopodobnie TSR. Ruch jest jednak powolny.

Edytuj 15/09/2018

Projekt TS został wysłany do organów krajowych w celu głosowania.

Tekst można znaleźć tutaj: https://github.com/cplusplus/reflection-ts

Edytuj 11/07/2019

Refleksja TS jest już ukończona i można ją komentować i głosować latem (2019).

Podejście programistyczne dla meta-szablonów należy zastąpić prostszym podejściem do kompilowania kodu czasu (nie odzwierciedlonym w TS).

Edytuj 10/02/2020

Tutaj jest prośba o wsparcie TS refleksji w Visual Studio:

Dyskusja na temat TS autor David Sankel:

Edytuj 17 marca 2020 r

Postępuje refleksja. Raport z „Raportu z podróży komitetu ISO C ++ w Pradze 2020-02” można znaleźć tutaj:

Szczegółowe informacje na temat tego, co jest rozważane w C ++ 23, można znaleźć tutaj (zawiera krótką sekcję Odbicie):

Edytuj 4 czerwca 2020 r

Jeff Preshing wydał nową platformę o nazwie „Plywood”, która zawiera mechanizm odzwierciedlania środowiska wykonawczego. Więcej informacji można znaleźć tutaj:

Narzędzia i podejście wydają się być jak dotąd najbardziej dopracowane i najłatwiejsze w użyciu.

Damian Dixon
źródło
1
Łącze cern jest zepsute.
Mostowski Zwiń
linki cern powinny być teraz naprawione. Mają tendencję do pękania dość często, co jest bólem.
Damian Dixon
Czy ta odpowiedź dotyczy tylko odbicia w czasie kompilacji?
einpoklum
@einpoklum jedynymi obecnie dostępnymi rozwiązaniami do refleksji są czas kompilacji, zwykle z kodem meta-szablonu lub makr. Najnowszy szkic TS wygląda na to, że powinien działać w środowisku wykonawczym, ale będziesz musiał zbudować wszystkie biblioteki z odpowiednim kompilatorem, aby zapisać niezbędne metadane.
Damian Dixon
@DamianDixon: To nieprawda. Istnieje kilka bibliotek refleksji w czasie wykonywania. Oczywiście, są raczej niezgrabne i są albo opt-in, albo wymagają modyfikacji kompilatora, ale nadal istnieją. Jeśli, jak rozumiem twój komentarz, odwoływałeś się tylko do refleksji podczas kompilacji, edytuj swoją odpowiedź, aby była jaśniejsza.
einpoklum
15

Musisz spojrzeć na to, co próbujesz zrobić i czy RTTI spełni twoje wymagania. Zaimplementowałem własne pseudorefleksję do bardzo konkretnych celów. Na przykład kiedyś chciałem móc elastycznie konfigurować dane wyjściowe symulacji. Wymagało to dodania kodu typu „kocioł” do klas, które byłyby wyprowadzane:

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

Pierwsze wywołanie dodaje ten obiekt do systemu filtrującego, który wywołuje BuildMap()metodę, aby dowiedzieć się, jakie metody są dostępne.

Następnie w pliku konfiguracyjnym możesz zrobić coś takiego:

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

Dzięki magii związanej z szablonami boost, jest to tłumaczone na serię wywołań metod w czasie wykonywania (gdy plik konfiguracyjny jest czytany), więc jest dość wydajne. Nie poleciłbym tego robić, chyba że naprawdę potrzebujesz, ale kiedy to zrobisz, możesz zrobić naprawdę fajne rzeczy.

KeithB
źródło
Uwielbiam te funkcje, które zawsze zwracają prawdę;) Zakładam, że jest to odporne na problemy z statycznym porządkowaniem inicjacji?
paulm
14

Poleciłbym używać Qt .

Istnieje licencja typu open source, a także licencja komercyjna.

Jérôme
źródło
1
Patrzyłem na to, ale używa makr i kod źródłowy wymaga analizy, aby wygenerować kod metadanych. Chciałbym uniknąć tego dodatkowego kroku. Wolałbym użyć biblioteki C ++ lub prostych makr. Dzięki za pomysł.
Nick
10
QT lub innej biblioteki wdrażaniu podobnego podejścia jest najlepszy masz zamiar dostać
jalf
5
Płać w czasie kompilacji lub płać w czasie wykonywania - tak czy inaczej płacisz!
Martin Beckett,
13

Co próbujesz zrobić z refleksją?
Można użyć Boost cechy typu i typeof bibliotek jako ograniczonej formie refleksji kompilacji. Oznacza to, że możesz sprawdzać i modyfikować podstawowe właściwości typu przekazywanego do szablonu.

Ferruccio
źródło
13

EDYCJA : CAMP nie jest już utrzymywany; dostępne są dwa widelce:

  • Jeden nazywa się również CAMP i jest oparty na tym samym interfejsie API.
  • Zastanowienie się jest częściowym przepisaniem i powinno być preferowane, ponieważ nie wymaga wzmocnienia; używa C ++ 11.

CAMP to licencjonowana biblioteka MIT (wcześniej LGPL), która dodaje refleksji do języka C ++. Nie wymaga określonego kroku wstępnego przetwarzania w kompilacji, ale powiązanie musi zostać wykonane ręcznie.

Obecna biblioteka Tegesoft korzysta ze Wzmocnienia, ale istnieje również rozwidlenie używające C ++ 11, które nie wymaga już Wzmocnienia .

filant
źródło
11

Zrobiłem kiedyś coś, czego szukasz, i chociaż można uzyskać pewien poziom refleksji i dostęp do funkcji wyższego poziomu, ból głowy związany z konserwacją może nie być tego wart. Mój system został wykorzystany do całkowitego oddzielenia klas interfejsu użytkownika od logiki biznesowej poprzez delegację podobną do koncepcji Objective-C przekazywania i przekazywania wiadomości. Można to zrobić, tworząc klasę bazową, która jest zdolna do odwzorowywania symboli (użyłem puli ciągów, ale można to zrobić za pomocą wyliczeń, jeśli wolisz obsługę błędów błędów w czasie kompilacji nad całkowitą elastycznością) niż wskaźniki funkcji (w rzeczywistości nie wskaźniki funkcji czystych, ale coś podobnego do tego, co Boost ma z Boost.Funkcją - do której wtedy nie miałem dostępu). Możesz zrobić to samo dla zmiennych składowych, o ile masz jakąś wspólną klasę podstawową zdolną do reprezentowania dowolnej wartości. Cały system stanowił bezproblemowe wyłudzanie Kodowania i Delegowania Kluczowych Wartości z kilkoma efektami ubocznymi, które być może były warte tyle czasu, ile potrzeba, aby każda klasa, która używała systemu, dopasowała wszystkie swoje metody i członków do legalnych wezwań : 1) Każda klasa może wywoływać dowolną metodę z dowolnej innej klasy bez konieczności dołączania nagłówków lub pisania fałszywych klas podstawowych, aby interfejs mógł zostać wstępnie zdefiniowany dla kompilatora; oraz 2) Obiekty pobierające i ustawiające zmiennych składowych były łatwe do zapewnienia bezpieczeństwa wątków, ponieważ zmienianie lub uzyskiwanie dostępu do ich wartości odbywało się zawsze za pomocą 2 metod w klasie bazowej wszystkich obiektów. Cały system stanowił bezproblemowe wyłudzanie Kodowania i Delegowania Kluczowych Wartości z kilkoma efektami ubocznymi, które być może były warte tyle czasu, ile potrzeba, aby każda klasa, która używała systemu, dopasowała wszystkie swoje metody i członków do legalnych wezwań : 1) Każda klasa może wywoływać dowolną metodę z dowolnej innej klasy bez konieczności dołączania nagłówków lub pisania fałszywych klas podstawowych, aby interfejs mógł zostać wstępnie zdefiniowany dla kompilatora; oraz 2) Obiekty pobierające i ustawiające zmiennych składowych były łatwe do zapewnienia bezpieczeństwa wątków, ponieważ zmienianie lub uzyskiwanie dostępu do ich wartości odbywało się zawsze za pomocą 2 metod w klasie bazowej wszystkich obiektów. Cały system stanowił bezproblemowe wyłudzanie Kodowania i Delegowania Kluczowych Wartości z kilkoma efektami ubocznymi, które być może były warte tyle czasu, ile potrzeba, aby każda klasa, która używała systemu, dopasowała wszystkie swoje metody i członków do legalnych wezwań : 1) Każda klasa może wywoływać dowolną metodę z dowolnej innej klasy bez konieczności dołączania nagłówków lub pisania fałszywych klas podstawowych, aby interfejs mógł zostać wstępnie zdefiniowany dla kompilatora; oraz 2) Obiekty pobierające i ustawiające zmiennych składowych były łatwe do zapewnienia bezpieczeństwa wątków, ponieważ zmienianie lub uzyskiwanie dostępu do ich wartości odbywało się zawsze za pomocą 2 metod w klasie bazowej wszystkich obiektów. 1) Każda klasa może wywołać dowolną metodę z dowolnej innej klasy bez konieczności dołączania nagłówków lub pisania fałszywych klas podstawowych, aby interfejs mógł zostać wstępnie zdefiniowany dla kompilatora; oraz 2) Obiekty pobierające i ustawiające zmiennych składowych były łatwe do zapewnienia bezpieczeństwa wątków, ponieważ zmienianie lub uzyskiwanie dostępu do ich wartości odbywało się zawsze za pomocą 2 metod w klasie bazowej wszystkich obiektów. 1) Każda klasa może wywołać dowolną metodę z dowolnej innej klasy bez konieczności dołączania nagłówków lub pisania fałszywych klas podstawowych, aby interfejs mógł zostać wstępnie zdefiniowany dla kompilatora; oraz 2) Obiekty pobierające i ustawiające zmiennych składowych były łatwe do zapewnienia bezpieczeństwa wątków, ponieważ zmienianie lub uzyskiwanie dostępu do ich wartości odbywało się zawsze za pomocą 2 metod w klasie bazowej wszystkich obiektów.

Doprowadziło to również do możliwości robienia naprawdę dziwnych rzeczy, które w przeciwnym razie nie byłyby łatwe w C ++. Na przykład mogłem utworzyć obiekt Array, który zawierał dowolne elementy dowolnego typu, w tym siebie, i dynamicznie tworzyć nowe tablice, przekazując komunikat do wszystkich elementów tablicy i zbierając wartości zwracane (podobne do map w Lisp). Inną była implementacja obserwacji klucz-wartość, dzięki której mogłem skonfigurować interfejs użytkownika, aby natychmiast reagował na zmiany w elementach klas zaplecza zamiast ciągłego odpytywania danych lub niepotrzebnego przerysowywania wyświetlania.

Być może bardziej interesujący jest dla ciebie fakt, że możesz również zrzucić wszystkie metody i elementy zdefiniowane dla klasy, i nie mniej niż w formie łańcucha.

Wady systemu, które mogą zniechęcać do zawracania sobie głowy: dodawanie wszystkich wiadomości i kluczowych wartości jest niezwykle uciążliwe; jest wolniejszy niż bez odbicia; będzie wzrastać do nienawidzić widząc boost::static_pointer_casti boost::dynamic_pointer_castcałym swoim kodzie z gwałtownej namiętności; ograniczenia silnie typowanego systemu są nadal obecne, tak naprawdę po prostu je trochę ukrywasz, więc nie jest to takie oczywiste. Literówki w strunach również nie są zabawną ani łatwą do odkrycia niespodzianką.

Co do tego, jak zaimplementować coś takiego: po prostu użyj wspólnych i słabych wskaźników do wspólnej bazy (moja była bardzo pomysłowo nazywana „Obiektem”) i uzyskaj wszystkie typy, których chcesz użyć. Poleciłbym zainstalować Boost.Function zamiast robić to tak, jak ja to zrobiłem, z jakimś niestandardowym badziewiem i mnóstwem brzydkich makr do zawijania wywołań wskaźnika funkcji. Ponieważ wszystko jest mapowane, sprawdzanie obiektów jest tylko kwestią iteracji przez wszystkie klucze. Ponieważ moje zajęcia były zasadniczo tak blisko bezpośredniego oderwania kakao, jak to możliwe przy użyciu tylko C ++, jeśli chcesz czegoś takiego, sugerowałbym użycie dokumentacji kakao jako planu.

Michel
źródło
Hej, Michael; nadal masz do tego kod źródłowy, czy się go pozbyłeś? Chciałbym rzucić na to okiem, jeśli nie masz nic przeciwko.
RandomDSdevel
Ups, źle przeliterowałeś swoje imię! Nic dziwnego, że nigdy nie otrzymałem odpowiedzi…
RandomDSdevel
10

Istnieje kolejna nowa biblioteka do refleksji w C ++, o nazwie RTTR (Refleksja czasu wykonania, patrz także github ).

Interfejs jest podobny do odbicie w C # i działa bez żadnych RTTI.

Zack
źródło
8

Dwa podobne do refleksji rozwiązania, które znam z moich dni w C ++:

1) Użyj RTTI, który zapewni Ci bootstrap, aby zbudować swoje zachowanie przypominające odbicie, jeśli jesteś w stanie uzyskać wszystkie swoje klasy, aby wywodziły się z klasy podstawowej „obiektowej”. Ta klasa mogłaby udostępnić niektóre metody, takie jak GetMethod, GetBaseClass itp. Jeśli chodzi o sposób działania tych metod, musisz ręcznie dodać niektóre makra, aby udekorować typy, które za kulisami tworzą metadane w typie, aby dostarczyć odpowiedzi na GetMethods itp.

2) Inną opcją, jeśli masz dostęp do obiektów kompilatora, jest użycie zestawu DIA SDK . Jeśli dobrze pamiętam, pozwala to na otwarcie pdbs, które powinno zawierać metadane dla twoich typów C ++. Może być wystarczające, aby zrobić to, czego potrzebujesz. Ta strona pokazuje, w jaki sposób można uzyskać na przykład wszystkie podstawowe typy klas.

Oba te rozwiązania są jednak trochę brzydkie! Nie ma nic takiego jak trochę C ++, abyś docenił luksus C #.

Powodzenia.

użytkownik4385
źródło
To podstępny i gigantyczny hack z zaproponowaną tam funkcją DIA SDK.
Sqeaky
7

EDYCJA: Zaktualizowano niedziałający link 7 lutego 2017 r.

Myślę, że nikt o tym nie wspominał:

W CERN używają systemu pełnego odbicia dla C ++:

CERN Reflex . Wygląda na to, że działa bardzo dobrze.

Germán Diago
źródło
@ j4nbur53 Link jest zepsuty, ponieważ wydaje się, że osiągnął kamień milowy: root.cern.ch
Germán Diago
Czy to możliwe, że masz na myśli ten link root.cern.ch/root/doc/ROOTUsersGuideHTML/ch07.html Refleks rozdziału?
Mostowski Zwiń
Spróbuj tego root.cern.ch/how/how-use-reflex . Reflex działa jako generator, który analizuje pliki nagłówkowe i generuje kod / bibliotekę introspekcji c ++, którą można połączyć i użyć prostego interfejsu API.
Adam Ryczkowski
6

To pytanie jest już trochę stare (nie wiem, dlaczego wciąż uderzam w stare pytania), ale myślałem o BOOST_FUSION_ADAPT_STRUCT, który wprowadza refleksję w czasie kompilacji.

Od Ciebie zależy mapowanie tego na odbicie w czasie rzeczywistym i nie będzie to zbyt łatwe, ale jest to możliwe w tym kierunku, podczas gdy nie byłoby odwrotnie :)

Naprawdę uważam, że makro do kapsułkowania tego BOOST_FUSION_ADAPT_STRUCTmoże wygenerować niezbędne metody, aby uzyskać zachowanie w czasie wykonywania.

Matthieu M.
źródło
2
autor: minghua (który pierwotnie edytował post): Przekopałem się do tego rozwiązania BOOST_FUSION_ADAPT_STRUCT i ostatecznie wymyśliłem przykład. Zobacz to nowsze pytanie SO - C ++ iteruje w zagnieżdżone pole struktury z funkcją boost fusion adapt_struct .
Matthieu M.
Świetnie, Matthieu! Właśnie zdałem sobie sprawę, że widziałem twoje wskazówki tu i tam w ciągu ostatniego roku. Nie zauważyłem, że są do tej pory spokrewnieni. To były bardzo inspirujące.
minghua
6

Myślę, że może zainteresować Cię artykuł „Korzystanie z szablonów do refleksji w C ++” autorstwa Dominica Filiona. Znajduje się w sekcji 1.4 Klejnotów do programowania gier 5 . Niestety nie mam przy sobie swojej kopii, ale poszukaj jej, ponieważ myślę, że wyjaśnia, o co prosisz.

Luis
źródło
4

Rozważać jest biblioteką refleksyjną C ++, w odpowiedzi na to pytanie. Rozważyłem opcje i zdecydowałem się stworzyć własną, ponieważ nie mogłem znaleźć takiej, która zaznaczyłaby wszystkie moje pola.

Chociaż są świetne odpowiedzi na to pytanie, nie chcę używać ton makr ani polegać na Boost. Boost to świetna biblioteka, ale istnieje wiele małych projektów C ++ 0x na zamówienie, które są prostsze i mają krótszy czas kompilacji. Są też zalety dekorowania klasy zewnętrznie, takie jak pakowanie biblioteki C ++, która (jeszcze?) Nie obsługuje C ++ 11. To rozwidlenie CAMP, używające C ++ 11, które nie wymaga już wzmocnienia .

Nacięcie
źródło
4

Refleksja dotyczy zasadniczo tego, co kompilator postanowił pozostawić jako ślady w kodzie, który kod wykonawczy może zapytać. C ++ słynie z tego, że nie płaci za to, czego nie używasz; ponieważ większość ludzi nie używa / nie chce refleksji, kompilator C ++ unika kosztów, nie rejestrując niczego .

C ++ nie zapewnia refleksji i nie jest łatwo „zasymulować” go tak ogólnie, jak zauważyły ​​to inne odpowiedzi.

W obszarze „inne techniki”, jeśli nie masz języka z refleksją, uzyskaj narzędzie, które może wyodrębnić potrzebne informacje w czasie kompilacji.

Nasz pakiet Deng Software Reengineering Toolkit to uogólniona technologia kompilatora sparametryzowana wyraźnymi definicjami języka. Ma definicje języka dla C, C ++, Java, COBOL, PHP, ...

W przypadku wersji C, C ++, Java i COBOL zapewnia pełny dostęp do drzew parsowanych i informacji o tablicy symboli. Ta informacja w tablicy symboli zawiera dane, których prawdopodobnie potrzebujesz od „refleksji”. Jeśli Twoim celem jest wyliczenie zestawu pól lub metod i zrobienie czegoś z nimi, DMS może zostać użyty do przekształcenia kodu zgodnie z tym, co znajdziesz w tabelach symboli w dowolny sposób.

Ira Baxter
źródło
3

Możesz znaleźć inną bibliotekę tutaj: http://www.garret.ru/cppreflection/docs/reflect.html Obsługuje 2 sposoby: uzyskanie informacji o typie z informacji debugowania i pozwolenie programiście na dostarczenie tych informacji.

Zainteresowałem się również refleksją nad moim projektem i znalazłem tę bibliotekę, jeszcze jej nie wypróbowałem, ale wypróbowałem inne narzędzia tego faceta i podoba mi się, jak działają :-)

alariq
źródło
3

Sprawdź Classdesc http://classdesc.sf.net . Zapewnia odbicie w postaci „deskryptorów” klasy, współpracuje z dowolnym standardowym kompilatorem C ++ (tak, wiadomo, że współpracuje z Visual Studio oraz GCC) i nie wymaga adnotacji kodu źródłowego (chociaż istnieją pewne pragmy do obsługi trudnych sytuacji ). Jest rozwijany od ponad dekady i wykorzystywany w wielu projektach na skalę przemysłową.

Russell Standish
źródło
1
Witamy w Stack Overflow. Chociaż ta odpowiedź dotyczy tematu, należy zaznaczyć, że jesteś autorem tego oprogramowania, aby było jasne, że nie jest to bezstronna rekomendacja :-)
Matthew Strawbridge
2

Kiedy chciałem się zastanowić w C ++, przeczytałem ten artykuł i poprawiłem to, co tam zobaczyłem. Niestety nie ma. Nie jestem właścicielem wyniku ... ale z pewnością możesz dostać to, co miałem i stamtąd.

Obecnie badam, kiedy mam na to ochotę, metody użycia inherit_linearly, aby znacznie ułatwić definiowanie typów odblaskowych. Zaszedłem dość daleko, ale wciąż mam wiele do zrobienia. Bardzo prawdopodobne jest, że zmiany w C ++ 0x będą bardzo pomocne w tym obszarze.

Edward Strange
źródło
2

Wygląda na to, że C ++ nadal nie ma tej funkcji. I C ++ 11 również odroczył refleksję ((

Wyszukaj niektóre makra lub utwórz własne. Qt może również pomóc w odbiciu (jeśli można go użyć).

Bohdan
źródło
2

nawet jeśli odbicie nie jest obsługiwane w c ++, nie jest zbyt trudne do wdrożenia. Spotkałem ten świetny artykuł: http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

artykuł szczegółowo wyjaśnia, w jaki sposób można wdrożyć dość prosty i podstawowy system refleksji. przyznał, że nie jest to najzdrowsze rozwiązanie, i zostały jeszcze szorstkie krawędzie do rozwiązania, ale na moje potrzeby było to wystarczające.

dolna linia - odbicie może się opłacić, jeśli wykonane poprawnie, i jest to całkowicie wykonalne w c ++.

Naore Azenkut
źródło
2

Chciałbym reklamować istnienie automatycznego zestawu narzędzi introspekcji / refleksji „IDK”. Korzysta z meta-kompilatora takiego jak Qt i dodaje meta informacje bezpośrednio do plików obiektowych. Twierdzi się, że jest łatwy w użyciu. Brak zależności zewnętrznych. Pozwala nawet automatycznie odzwierciedlać std :: string, a następnie używać go w skryptach. Proszę spojrzeć na IDK

Eugene G.
źródło
2

Jeśli szukasz względnie prostej refleksji w C ++ - zebrałem z różnych źródeł makro / definicje i skomentowałem ich działanie. Możesz pobrać pliki nagłówkowe stąd:

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

zestaw definicji, a na dodatek funkcjonalność:

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

Przykładowa aplikacja znajduje się również w repozytorium git, tutaj: https://github.com/tapika/TestCppReflect/

Częściowo skopiuję go tutaj z wyjaśnieniem:

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

}

REFLECTABLEZdefiniuj używa nazwy klasy + nazwy pola za pomocą offsetof- do określenia, w którym miejscu w pamięci znajduje się dane pole. Próbowałem podnieść NET terminologia tak daleko jak to możliwe, ale C ++ i C # są różne, więc nie jest to 1 do 1. Całe c ++ mieszka odbicie model in TypeInfoi FieldInfoklas.

Użyłem parsera pugi xml, aby pobrać kod demo do xml i przywrócić go z xml.

Dane wyjściowe wygenerowane przez kod demonstracyjny wyglądają następująco:

<?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>

Możliwe jest również włączenie dowolnej obsługi klasy / struktury innej firmy za pośrednictwem klasy TypeTraits i częściowej specyfikacji szablonu - w celu zdefiniowania własnej klasy TypeTraitsT, w podobny sposób jak CString lub int - patrz przykładowy kod w

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

To rozwiązanie dotyczy systemu Windows / Visual studio. Możliwe jest przeniesienie go do innych systemów operacyjnych / kompilatorów, ale jeszcze tego nie zrobił. (Zapytaj mnie, czy naprawdę podoba ci się rozwiązanie, może mogę ci pomóc)

To rozwiązanie ma zastosowanie do serializacji w jednym ujęciu jednej klasy z wieloma podklasami.

Jeśli jednak szukasz mechanizmu szeregowania części klasy lub nawet kontrolowania, jakie funkcje wywołują odbicia refleksyjne, możesz rzucić okiem na następujące rozwiązanie:

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

Bardziej szczegółowe informacje można znaleźć w filmie na youtube:

Odbicie typu środowiska wykonawczego C ++ https://youtu.be/TN8tJijkeFE

Próbuję wyjaśnić nieco głębiej, jak będzie działać odbicie c ++.

Przykładowy kod będzie wyglądał na przykład tak:

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;

Ale każdy krok tutaj powoduje wywołanie funkcji Przy użyciu właściwości C ++ z __declspec(property(get =, put ... ).

który otrzymuje pełne informacje o typach danych C ++, nazwach właściwości C ++ i wskaźnikach instancji klas, w formie ścieżki, i na podstawie tych informacji można wygenerować xml, json lub nawet serializować je przez Internet.

Przykłady takich wirtualnych funkcji zwrotnych można znaleźć tutaj:

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

Zobacz funkcje ReflectCopyi funkcję wirtualną ::OnAfterSetProperty.

Ale ponieważ temat jest bardzo zaawansowany - polecam najpierw przejrzeć wideo.

Jeśli masz jakieś pomysły na ulepszenia, skontaktuj się ze mną.

TarmoPikaro
źródło
1

Odbicie w C ++ jest bardzo przydatne, w przypadkach, w których musisz uruchomić jakąś metodę dla każdego członka (na przykład: serializacja, haszowanie, porównywanie). Przyszedłem z ogólnym rozwiązaniem o bardzo prostej składni:

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

Gdzie ENUMERATE_MEMBERS to makro, które zostanie opisane później (AKTUALIZACJA):

Załóżmy, że zdefiniowaliśmy funkcję serializacji dla int i std :: string w następujący sposób:

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

I mamy ogólną funkcję w pobliżu „tajnego makra”;)

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

Teraz możesz pisać

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)

Zatem mając makro ENUMERATE_MEMBERS w definicji struktury, możesz tworzyć serializację, porównywać, mieszać i inne rzeczy bez dotykania oryginalnego typu, jedynym wymaganiem jest implementacja metody „EnumerateWith” dla każdego typu, który nie jest wyliczalny dla każdego modułu wyliczającego (jak BinaryWriter) . Zwykle będziesz musiał wdrożyć 10-20 „prostych” typów, aby obsłużyć dowolny typ w swoim projekcie.

To makro powinno mieć zerowy narzut w stosunku do tworzenia / niszczenia struktur w czasie wykonywania, a kod T.EnumerateWith () powinien być generowany na żądanie, co można osiągnąć przez utworzenie funkcji wstawiania szablonu, więc jedyny narzut w cała historia polega na dodaniu ENUMERATE_MEMBERS (m1, m2, m3 ...) do każdej struktury, podczas gdy implementacja określonej metody dla każdego typu elementu jest koniecznością w każdym rozwiązaniu, więc nie zakładam, że jest to narzut.

AKTUALIZACJA: Istnieje bardzo prosta implementacja makra ENUMERATE_MEMBERS (jednak można ją nieco rozszerzyć, aby obsługiwała dziedziczenie po strukturze wyliczalnej)

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

I nie potrzebujesz żadnej biblioteki innej firmy dla tych 15 linii kodu;)

Jenny
źródło
1

Możesz uzyskać fajne funkcje odbicia statycznego dla struktur za pomocą BOOST_HANA_DEFINE_STRUCT z biblioteki Boost :: Hana.
Hana jest dość wszechstronna, nie tylko pod kątem zamierzonego zastosowania, ale także w przypadku wielu metaprogramowania szablonów.

nnolte
źródło
1

Random Access Reflection zapewnia dość łatwą i intuicyjną refleksję - wszystkie informacje o polu / typie są zaprojektowane tak, aby były dostępne w tablicach lub czuły się jak dostęp do tablicy. Jest napisany dla C ++ 17 i współpracuje z Visual Studios, g ++ i Clang. Biblioteka ma tylko nagłówek, co oznacza, że ​​potrzebujesz tylko skopiować plik „Reflect.h” do projektu, aby go użyć.

Odbite struktury lub klasy wymagają makra REFLECT, w którym podajesz nazwę klasy, którą odzwierciedlasz oraz nazwy pól.

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

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

To wszystko, nie potrzebujesz dodatkowego kodu, aby ustawić odbicie. Opcjonalnie możesz podać nadklasy (w nawiasach pierwszego argumentu) i adnotacje pól (w nawiasach poprzedzających pole, które chcesz opisać), aby móc przechodzić nadklasy lub dodawać dodatkowe informacje o czasie kompilacji do pola (takiego jak Json: :Ignorować).

Pętla przez pola może być tak prosta jak ...

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

Możesz przejść przez instancję obiektu, aby uzyskać dostęp do wartości pól (które możesz odczytać lub zmodyfikować) i informacji o typach pól ...

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

JSON biblioteka jest zbudowany na szczycie RandomAccessReflection które auto identyfikuje odpowiednie środki wyjściowe JSON do czytania lub pisania, a może rekurencyjnie przechodzić żadnych odbitych pól, a także tablice i kontenery 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);
}

Powyższe można uruchomić tak ...

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

Zobacz też...

jjf28
źródło
0

Jeśli zadeklarujesz wskaźnik do funkcji takiej jak ta:

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

Możesz przypisać miejsce w pamięci do tej funkcji w ten sposób (wymaga libdli 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);
}

Aby załadować symbol lokalny za pomocą pośrednictwa, możesz użyć dlopenna wywołanie binarne ( argv[0]).

Jedynym wymaganiem (innym niż dlopen(), libdli dlfcn.h) jest znajomość argumentów i rodzaju funkcji.

SS Anne
źródło