Jak dodać reflection do aplikacji C++?

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ę tu o natywnym C++, a nie zarządzanym C++, który ma odbicie. Zdaję sobie sprawę, że C++ dostarcza pewnych ograniczonych informacji za pomocą RTTI. Jakie dodatkowe biblioteki (lub inne techniki) mogłyby dostarczyć tych informacji?

Author: Lazer, 2008-09-03

30 answers

To, co musisz zrobić, to poprosić preprocesor o wygenerowanie danych odbicia pól. Dane te mogą być przechowywane jako zagnieżdżone klasy.

Najpierw, aby ułatwić i ułatwić zapisanie go w preprocesorze, użyjemy typed expression. Wpisane wyrażenie jest tylko wyrażeniem, które umieszcza typ w nawiasie. Więc zamiast pisać int x napiszesz (int) x. Oto kilka przydatnych makr, które pomogą w pisaniu 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 makro REFLECTABLE na Wygeneruj dane o każdym polu (plus samo pole). To makro będzie nazywane tak:

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

Więc używając Boost.PP iterujemy nad każdym argumentem 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)); \
    } \
}; \

To, co robi, to generowanie stałej fields_n, która jest liczbą odblaskowych pól w klasie. Następnie specjalizuje się field_data dla każdego pola. Posiada również klasę reflector, dzięki czemu może uzyskać dostęp do pól nawet wtedy, 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 nad polami używamy wzorca odwiedzającego. Tworzymy zakres MPL od 0 do liczby pól i uzyskujemy dostęp do danych pól w tym indeksie. Następnie przekazuje dane pola do odwiedzającego 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 na chwilę prawdy, poskładamy to wszystko do kupy. Oto jak możemy zdefiniować klasęPerson, która jest odblaskowa:

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

Oto uogólnionaprint_fields funkcja wykorzystująca dane odbicia do iteracji pól:

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_fields z klasą refleksyjną Person:

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

Które wyjście:

name=Tom
age=82

I voila, właśnie zaimplementowaliśmy reflection w C++, w mniej niż 100 linijkach kodu.

 203
Author: Paul Fultz II,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-20 16:45:45

Istnieją dwa rodzaje reflection pływających wokół.

  1. kontrola poprzez iterację nad członkami typu, wyliczanie jego metod i tak dalej.

    Nie jest to możliwe w C++.
  2. kontrola poprzez sprawdzenie, czy typ klasy (class, struct, union) ma metodę lub typ zagnieżdżony, pochodzi z innego konkretnego typu.

    Tego typu rzeczy są możliwe w C++ używając template-tricks. Użycie boost::type_traits dla wielu rzeczy(np. sprawdzanie, czy typ jest całkowy). Na sprawdzanie istnienia funkcji member, użyj Czy można napisać szablon, aby sprawdzić istnienie funkcji? . Aby sprawdzić, czy istnieje określony typ zagnieżdżony, użyj plain SFINAE .

Jeśli szukasz sposobów na osiągnięcie 1), jak sprawdzanie, ile metod ma klasa, lub jak uzyskanie reprezentacji ciągów znaków id klasy, to obawiam się, że nie ma na to standardowego sposobu C++. Musisz użyć

  • A Meta Compiler podobnie jak Qt Meta Object Compiler, który tłumaczy Twój kod dodając dodatkowe meta informacje.
  • Framework zawierający makra, które pozwalają na dodanie wymaganych metainformacji. Musisz podać frameworkowi wszystkie metody, nazwy klas, klasy bazowe i wszystko, czego potrzebuje.
C++ tworzy się z myślą o szybkości. Jeśli chcesz inspekcji wysokiego poziomu, jak C # lub Java ma, to obawiam się, że muszę ci powiedzieć, że nie ma sposobu bez jakiegoś wysiłku.
 96
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:45
 55
Author: Brad Wilson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-03 11:04:56

Informacje istnieją - ale nie w formacie, którego potrzebujesz i tylko wtedy, gdy wyeksportujesz swoje klasy. To działa w Windows, nie wiem jak inne platformy. Użycie specyfikacji storage-class jak w, na przykład:

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

To sprawia, że kompilator buduje dane definicji klasy do DLL / Exe. Ale nie jest w formacie, który można łatwo wykorzystać do refleksji.

W mojej firmie zbudowaliśmy bibliotekę, która interpretuje te metadane i pozwala na odzwierciedlenie klasy bez wstawianie dodatkowych makr itp. do samej klasy. Pozwala na wywołanie funkcji w następujący sposób:

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

To skutecznie robi:

instance_ptr->Foo(1.331);

The Invoke (this_pointer,...) funkcja ma zmienne argumenty. Oczywiście wywołując funkcję w ten sposób omijasz rzeczy takie jak const-safety i tak dalej, więc te aspekty są zaimplementowane jako testy uruchomieniowe.

Jestem pewien, że składnia może zostać poprawiona, i to działa tylko na Win32 i Win64 póki co. Uznaliśmy, że jest to naprawdę przydatne dla posiadanie automatycznych interfejsów GUI do klas, tworzenie właściwości w C++, przesyłanie strumieniowe do i z XML i tak dalej, i nie ma potrzeby czerpania z określonej klasy bazowej. Jeśli będzie wystarczająco dużo popytu, może uda nam się go wypuścić.

 36
Author: Roderick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-11 23:06:26

RTTI nie istnieje dla C++.

To jest po prostu złe. Właściwie sam termin "RTTI" został ukuty przez standard C++. Z drugiej strony, RTTI nie idzie zbyt daleko w realizacji refleksji.
 35
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-03 13:18:29

Musisz spojrzeć na to, co próbujesz zrobić i czy RTTI spełni Twoje wymagania. Zaimplementowałem własną pseudo-refleksję do bardzo konkretnych celów. Na przykład kiedyś chciałem być w stanie elastycznie skonfigurować to, co generuje symulacja. Wymagało to dodania kodu kotła do klas, które byłyby wyjściowe:

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 filtrowania, który wywołuje metodę BuildMap(), 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

Poprzez magię szablonów z udziałem boost, jest to tłumaczone na serię wywołań metod w czasie wykonywania (gdy plik konfiguracyjny jest odczytywany), więc jest to dość wydajne. Nie polecałbym tego robić, chyba że naprawdę musisz, ale kiedy to zrobisz, możesz zrobić naprawdę fajne rzeczy.

 14
Author: KeithB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-23 16:20:51

Co próbujesz zrobić z odbiciem?
Możesz użyć bibliotek Boost type traits i typeof jako ograniczonej formy odbicia w czasie kompilacji. Oznacza to, że można sprawdzić i zmodyfikować podstawowe właściwości typu przekazanego do szablonu.

 13
Author: Ferruccio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-03 11:33:05

Polecam użycie Qt .

Istnieje licencja open-source, jak również licencja komercyjna.

 13
Author: Jérôme,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-03 13:06:38

Edytuj: obóz nie jest już utrzymywany ; dostępne są dwa widelce:

  • jeden jest również nazywany CAMP i jest oparty na tym samym API.
  • Ponder jest częściowym przepisaniem i jest preferowany, ponieważ nie wymaga Boost ; używa C++11.

CAMP jest licencjonowaną biblioteką MIT (dawniej LGPL), która dodaje refleksję do języka C++. Nie wymaga określonego etapu wstępnego przetwarzania w kompilacji, ale oprawa musi być wykonana ręcznie.

[[0]}obecna biblioteka Tegesoft używa Boost, ale istnieje również fork używający C++11, który nie wymaga już Boost .
 11
Author: philant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-10 12:50:53

Kiedyś zrobiłem coś takiego, o co ci chodzi, i chociaż możliwe jest uzyskanie pewnego poziomu refleksji i dostępu do funkcji wyższego poziomu, ból głowy w utrzymaniu może nie być tego wart. Mój system został użyty do utrzymania klas interfejsu użytkownika całkowicie oddzielonych od logiki biznesowej poprzez delegację podobną do koncepcji Objective-C przekazywania i przekazywania wiadomości. Sposobem na to jest stworzenie jakiejś podstawowej klasy zdolnej do mapowania symboli (użyłem puli łańcuchów, ale można to zrobić z enums, jeśli wolisz szybkość i obsługę błędów w czasie kompilacji niż całkowitą elastyczność) do wskaźników funkcji (w rzeczywistości nie czystych wskaźników funkcji, ale coś podobnego do tego, co ma Boost Z Boost.Funkcja-do której wtedy nie miałem dostępu). Możesz zrobić to samo dla swoich zmiennych członkowskich, o ile masz jakąś wspólną klasę bazową zdolną reprezentować dowolną wartość. Cały system był niezakłóconym zrzutem kodowania i delegowania wartości klucza, z kilkoma efektami ubocznymi, które być może 1) każda klasa mogła wywołać dowolną metodę na dowolnej innej klasie bez konieczności dołączania nagłówków lub pisania fałszywych klas bazowych, aby interfejs mógł być predefiniowany dla kompilatora; oraz 2) gettery i settery zmiennych członkowskich były łatwe do uczynienia thread-safe, ponieważ zmiana lub dostęp do ich wartości była zawsze dokonywana za pomocą 2 metod w klasie bazowej. wszystkie obiekty.

Doprowadziło to również do możliwości robienia naprawdę dziwnych rzeczy, które w przeciwnym razie nie są łatwe w C++. Na przykład mogłem wytworzyć obiekt Array, który zawierał dowolne elementy dowolnego typu, łącznie z samym sobą, i tworzyć nowe tablice dynamicznie przekazując wiadomość do wszystkich elementów tablicy i zbierając zwracane wartości (podobnie jak map w Lispie). Kolejną była implementacja obserwacji wartości klucza, dzięki której byłem w stanie skonfigurować interfejs użytkownika, aby natychmiast reagował na zmiany w członkowie klas zaplecza zamiast ciągłego przepytywania danych lub niepotrzebnego przerysowywania wyświetlacza.

Być może bardziej interesujący jest fakt, że możesz również zrzucić wszystkie metody i członkowie zdefiniowane dla danej klasy, a nie mniej w formie łańcuchowej.

Wady systemu, które mogą zniechęcić cię do zawracania sobie głowy: dodawanie wszystkich wiadomości i kluczowych wartości jest niezwykle żmudne; jest wolniejsze niż bez żadnej refleksji; będziesz dorastać do nienawiści widząc boost::static_pointer_cast i boost::dynamic_pointer_cast na całym swoim kod z gwałtowną pasją; ograniczenia mocno wpisanego systemu nadal istnieją, naprawdę po prostu je trochę ukrywasz, więc nie jest to takie oczywiste. Literówki w Twoich ciągach nie są również zabawną lub łatwą do odkrycia niespodzianką.

Jak zaimplementować coś takiego: po prostu użyj współdzielonych i słabych wskaźników do jakiejś wspólnej bazy (mój był bardzo pomysłowo nazywany "obiektem") i wywołaj dla wszystkich typów, których chcesz użyć. Polecam zainstalować Boost.Funkcja zamiast robić to w ten sposób Zrobiłem, co było z jakimś niestandardowym badziewiem i mnóstwem brzydkich makr do zawijania wywołań wskaźników funkcji. Ponieważ wszystko jest mapowane, sprawdzanie obiektów jest tylko kwestią iteracji przez wszystkie klucze. Ponieważ moje zajęcia były w zasadzie tak bliskie bezpośredniemu zrzutowi Cocoa, jak to tylko możliwe, używając tylko C++, jeśli chcesz czegoś takiego, sugerowałbym użycie dokumentacji Cocoa jako schematu.

 10
Author: Michel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-27 21:41:28

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

1) Użyj RTTI, który zapewni Ci bootstrap do budowania Twojego zachowania przypominającego odbicie, jeśli jesteś w stanie sprawić, że wszystkie Twoje klasy wywodzą się z 'object' klasy bazowej. Klasa ta może dostarczać pewne metody jak GetMethod, GetBaseClass itp. Jeśli chodzi o sposób działania tych metod, musisz ręcznie dodać kilka makr do dekoracji typów, 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 DIA SDK. Jeśli dobrze pamiętam, to pozwala na otwieranie PDB, które powinny zawierać metadane dla typów C++. To może wystarczyć, by zrobić to, czego potrzebujesz. ta strona pokazuje jak można uzyskać wszystkie typy bazowe klasy na przykład.

Oba te rozwiązania są jednak trochę brzydkie! Nie ma to jak trochę C++, aby docenić luksusy C#.

Dobry Szczęście.

 9
Author: user4385,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-03 12:13:09

Istnieje kolejna nowa biblioteka do refleksji w C++, o nazwie RTTR (Run Time Type Reflection, Zobacz także github).

Interfejs jest podobny do reflection w C# i działa bez żadnego RTTI.

 7
Author: Zack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-15 10:22:51

[[0]}EDIT: Zaktualizowano uszkodzony link od lutego, 7th, 2017.

Chyba nikt o tym nie wspomniał:

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

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

 7
Author: Germán Diago,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-07 04:48:08

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

To od was zależy, czy mapujecie to oczywiście do odbicia w czasie biegu i nie będzie to zbyt łatwe, ale jest to możliwe w tym kierunku, a nie w odwrotnym kierunku:)

Myślę, że makro do enkapsulacji BOOST_FUSION_ADAPT_STRUCT może wygenerować niezbędne metody, aby uzyskać runtime zachowanie.

 6
Author: Matthieu M.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-12 17:05:53

Myślę, że może zainteresować Cię artykuł" wykorzystanie szablonów do refleksji w C++ " Dominika Filiona. Jest w sekcji 1.4 Game Programming Gems 5. Niestety nie mam mojej kopii ze mną, ale szukać go, ponieważ myślę, że wyjaśnia to, o co prosisz.

 5
Author: Luis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-03 15:15:45

Reflection nie jest obsługiwane przez C++ po wyjęciu z pudełka. Jest to smutne, ponieważ sprawia, że testowanie obronne jest bolesne.

Istnieje kilka podejść do robienia refleksji:

  1. Użyj informacji o debugowaniu (Nie przenośne).
  2. posypać swój kod makrami / szablonami lub innym podejściem źródłowym (wygląda brzydko)
  3. zmodyfikuj kompilator, taki jak clang / gcc, aby stworzyć 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 kilka 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 obecnie grupa robocza dla C++ reflection. Zobacz nowości dla C++14 @ CERN:

Edit 13/08/17: Od czasu pierwotnego postu nastąpił szereg potencjalnych postępów w odbiorze. Poniżej przedstawiono więcej szczegółów i omówiono różne techniki i status:

  1. statyczne odbicie w pigułce
  2. Odbicie Statyczne
  3. a design for static reflection

Jednak nie wygląda obiecując w niedalekiej przyszłości znormalizowane podejście do refleksji w C++, chyba że będzie dużo większe zainteresowanie ze strony społeczności wsparciem dla refleksji w C++.

Poniżej przedstawiono aktualny status na podstawie opinii z ostatniego spotkania standardów C++:

Edycja 13/12/2017

Odbicie wydaje się iść w kierunku C++ 20 lub więcej prawdopodobnie TSR. Ruch jest jednak powoli.

Edycja 15/09/2018

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

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

 5
Author: Damian Dixon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-15 12:15:05

Ponder jest biblioteką refleksji C++, w odpowiedzi na to pytanie. Rozważałem opcje i postanowiłem zrobić własne, ponieważ nie mogłem znaleźć takiego, który zaznaczył wszystkie moje pola.

Chociaż są świetne odpowiedzi na to pytanie, nie chcę używać ton makr, ani polegać na Boost. Boost jest świetną biblioteką, ale istnieje wiele małych projektów C++0x, które są prostsze i mają krótszy czas kompilacji. Są też zalety możliwości udekorowania klasy zewnętrznie, jak owijanie biblioteki C++, która nie (jeszcze?) obsługuje C++11. Jest to fork of CAMP, używając C++11, że nie wymaga już Boost .

 4
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-17 09:05:48

Refleksja jest zasadniczo o tym, co kompilator zdecydował się pozostawić jako footprinty w kodzie, który Kod uruchomieniowy może odpytywać. C++ słynie z tego, że nie płacisz za to, czego nie używasz; ponieważ większość ludzi nie używa / nie chce, kompilator C++ unika kosztów, nie nagrywając niczego.

Więc C++ nie dostarcza refleksji, i nie jest łatwo "symulować" go samemu jako ogólną zasadę, jak zauważyły inne odpowiedzi.

Pod "inne techniki", jeśli nie masz język z refleksją, Pobierz narzędzie, które może wyodrębnić informacje, które chcesz w czasie kompilacji.

Nasz DMS Software Reengineering Toolkit jest uogólnioną technologią kompilatora parametryzowaną przez wyraźne definicje langauge ' a. Zawiera definicje języków C, C++, Java, COBOL, PHP,...

Dla wersji C, C++, Java i COBOL zapewnia pełny dostęp do drzew parsowania i informacji o tabelach symboli. Informacja o tabeli symboli obejmuje rodzaj dane, które prawdopodobnie chcesz od "odbicia". Jeśli twoim celem jest wyliczenie jakiegoś zestawu pól lub metod i zrobić coś z nimi, DMS może być użyty do przekształcenia kodu zgodnie z tym, co znajdziesz w tabelach symboli w dowolny sposób.

 4
Author: Ira Baxter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-08 00:00:18

Możesz znaleźć inną bibliotekę tutaj: http://www.garret.ru/cppreflection/docs/reflect.html Obsługuje 2 sposoby: uzyskanie informacji typu z informacji debugowania i pozwolić programiście dostarczyć te informacje.

Również zainteresowałem się refleksją dla mojego projektu i znalazłem tę bibliotekę, jeszcze nie próbowałem, ale wypróbowałem Inne narzędzia od tego gościa i podoba mi się, jak działają: -)

 3
Author: alariq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-28 08:45:18

Sprawdź Classdesc http://classdesc.sf.net . zapewnia odbicie w postaci klas "deskryptorów", współpracuje z dowolnym standardowym kompilatorem C++ (tak, wiadomo, że współpracuje zarówno z Visual Studio, jak i GCC) i nie wymaga adnotacji kodu źródłowego (chociaż istnieją pewne pragmy, aby poradzić sobie z trudnymi sytuacjami). Jest rozwijany od ponad dekady i używany w wielu projektach na skalę przemysłową.

 3
Author: Russell Standish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-06 07:41:51

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

Obecnie badam, kiedy mam na to ochotę, metody używania inherit_linearly, aby znacznie ułatwić definiowanie typów odblaskowych. Zaszedłem w tym dość daleko, ale wciąż mam wiele do zrobienia. Zmiany w C++0x mogą być bardzo pomocne w tym miejsce.

 2
Author: Crazy Eddie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-14 04:12:23

Wygląda na to, że C++ nadal nie ma tej funkcji. Oraz C++11 przełożone odbicie też ((

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

 2
Author: Bohdan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-24 08:23:11

Spróbuj spojrzeć na ten projekt http://www.garret.ru/cppreflection/docs/reflect.html jest dodawany do C++. Dodała metadane do klas, których możesz następnie użyć.

 2
Author: Damian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-17 10:03:19

Mimo że reflection nie jest wspierane od razu w c++, nie jest zbyt trudne do zaimplementowania. Natknąłem się na ten świetny artykuł: http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

Artykuł wyjaśnia szczegółowo, w jaki sposób można wdrożyć dość prosty i podstawowy system refleksji. przyznam, że nie jest to najbardziej zdrowe rozwiązanie, a są szorstkie krawędzie do uporządkowania, ale dla moich potrzeb było to wystarczy.

The bottom line-reflection can Pae off if done correctly, and it is completely reable in c++.

 2
Author: Naore Azenkut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-02 23:19:14

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

 2
Author: Jen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-12 12:49:44

Reflection w C++ jest bardzo przydatne, w przypadkach, gdy trzeba uruchomić jakąś metodę dla każdego elementu (na przykład: serialization, hashing, compare). Przychodzę z ogólnym rozwiązaniem, z bardzo prostą 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 jest makrem opisanym później (UPDATE):

Załóżmy, że mamy zdefiniowaną 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 funkcję generyczną 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 napisz

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)

Mając makro ENUMERATE_MEMBERS w definicji struktury, można budować serializację, porównywać, hashować i inne rzeczy bez dotykania oryginalnego typu, jedynym wymogiem jest zaimplementowanie metody "EnumerateWith" dla każdego typu, który nie jest możliwy do wyliczenia, dla enumeratora (jak BinaryWriter). Zazwyczaj będziesz musiał wdrożyć 10-20 "prostych" typów, aby wspierać dowolny typ w projekcie.

To makro powinno mieć zerowy narzut, aby struct creation / destruction in run-time, a kod T. EnumerateWith () powinien być generowany na żądanie, co można osiągnąć, czyniąc go funkcją template-inline, więc jedynym nagłówkiem w całej historii jest dodanie ENUMERATE_MEMBERS (m1,m2,M3...) do każdej struktury, podczas gdy implementacja konkretnej metody Na Typ elementu jest koniecznością w każdym rozwiązaniu, więc nie zakładam jej jako napowietrznej.

UPDATE: Istnieje bardzo prosta implementacja makra ENUMERATE_MEMBERS (jednak można ją nieco rozszerzyć, aby wspierać dziedziczenie z 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);
}

A do tych 15 linijek kodu nie potrzebujesz żadnej biblioteki 3rd party;)

 1
Author: Evgeny Mamontov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-08 00:53:13

Projekt Root Reflex ma wsparcie dla tego.

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

 0
Author: Buğra Gedik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-17 02:15:34

Aktualizacja 24.2.2017

Wcześniej analizowałem wsparcie dla używania # define ' s i tak jak jest to zalecane w niektórych artykułach internetowych - trafiłem na defines w visual C++ nie działały identycznie w porównaniu z definicjami używanymi w gcc (na przykład w Internecie jest to dość często określane jako "MSVC walkaround"). Poza tym nie jestem w stanie łatwo zrozumieć, co dzieje się za maszyną do rozszerzania define / macro - trudno jest debugować każde rozszerzenie makr.

Istnieje kilka sposobów na obejście złożoności rozszerzenia define, jednym z nich jest włączenie flagi kompilatora "/ P " (tylko przed procesem do pliku) - po tym można porównać, jak otwiera się Twoje define. (Wcześniej używałem również aktywnego operatora stringfy (#))

Zebrałem wszystkie przydatne definicje z wielu forów, skorzystałem z nich i skomentowałem to, co dzieje się za machinery, tutaj znajdziesz cały plik nagłówkowy teraz:

Https://sourceforge.net/p/testcppreflect/code/HEAD/tree/MacroHelpers.h

Myślałem, że używanie tych makr do włączania odbicia w C++ jest dość trywialne, ale wymaga to nieco więcej magii,aby to zrobić.

Przypomniałem sobie działający przykładowy kod i umieściłem go jako projekt sourceforge, można pobrać tutaj: {]}

Https://sourceforge.net/p/testcppreflect/code/HEAD/tree/

Kod Demo wygląda jak to:

#include "CppReflect.h"
using namespace std;


class Person
{
public:
    REFLECTABLE( Person,
        (CString)   name,
        (int)       age
    )

};

class People
{
public:
    REFLECTABLE( People,
        (CString) groupName,
        (vector<Person>)  people
    )
};


void main(void)
{
    People ppl;
    ppl.groupName = "Group1";

    Person p;
    p.name = L"Roger";
    p.age = 37;
    ppl.people.push_back(p);
    p.name = L"Alice";
    p.age = 27;
    ppl.people.push_back( p );
    p.name = L"Cindy";
    p.age = 17;
    ppl.people.push_back( p );

    CStringA xml = ToXML( &ppl );
    CStringW errors;

    People ppl2;

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

}

REFLECTABLE define używa nazwy klasy + nazwy pola z offsetof - aby określić, w którym miejscu w pamięci znajduje się dane pole. Próbowałem podłapać terminologię. net na tyle, na ile to możliwe, ale C++ i C# są różne, więc nie jest to 1 do 1. Cały model odbicia C++ znajduje się w klasach TypeInfo i FieldInfo dla timebeingu, możliwe jest rozszerzenie wsparcia również na method, ale na razie postanowiłem zachować prostotę.

Użyłem parsera XML pugi do pobrania kod demo do xml i przywrócić go z powrotem z xml.

Więc wyjście wyprodukowane przez kod demo wygląda tak:

<?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 obsługi dowolnej klasy / struktury 3-rd poprzez klasę TypeTraits i częściową specyfikację szablonu-aby zdefiniować własną klasę TypeTraitsT, w podobny sposób jak CString lub int - zobacz przykładowy kod w

Https://sourceforge.net/p/testcppreflect/code/HEAD/tree/TypeTraits.h#l65

template <>
class TypeTraitsT<CString> : public TypeTraits
{
public:
    virtual CStringW ToString( void* pField )
    { 
        CString* s = (CString*)pField;
        return *s;
    }

    virtual void FromString( void* pField, const wchar_t* value )
    { 
        CString* s = (CString*)pField;
        *s = value;
    }
};

template <>
class TypeTraitsT<int> : public TypeTraits
{
public:
    virtual CStringW ToString( void* pField )
    {
        int* p = (int*) pField;
        return std::to_string(*p).c_str();
    }

    virtual void FromString( void* pField, const wchar_t* value )
    {
        int* p = (int*)pField;
        *p = _wtoi(value);
    }
};

Myślę, że tylko minusy mojego implementacja to użycie __if_exists - które może być rozszerzeniem specyficznym dla kompilatora Microsoft. Jeśli ktoś wie, jak to obejść, daj mi znać.

 0
Author: TarmoPikaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-24 22:55:43

Brak wbudowanego odbicia w C++ jest jedynym powodem, dla którego nowoczesne C++ nie jest używane do tworzenia stron internetowych (i brakuje ORM i innych frameworków)

Możesz spróbować http://www.extreme.indiana.edu/reflcpp/

 -2
Author: vlad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-15 18:26:36

Prostym sposobem jest użycie operatora dynamic_cast<>(), który po przypisaniu do niewłaściwego typu zwraca NULL, więc możesz w łatwy sposób przenieść się do konkretnej klasy bazowej, sprawdzając wartość wskaźnika, jeśli nie jest NULL, rzut został wykonany i otrzymałeś typ obiektu.

Ale jest to tylko proste rozwiązanie i podaje tylko typ obiektów, nie można zapytać, jakie ma metody, jak w Javie. Jeśli potrzebujesz zaawansowanego rozwiązania, istnieje kilka frameworków do wyboru.

 -4
Author: lurks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-18 21:42:05