2012-06-24 8 views
5

Próbuję napisać aplikację, która ładuje swoje rozszerzenia dynamicznie w czasie wykonywania. Użyłem biblioteki Preprocessor Boost do napisania funkcji preprocesora, która, biorąc pod uwagę listę nazw, deklaruje klasę dla każdej nazwy (i tworzy wszystkie podklasy klasy AbstractPlugin), a następnie deklaruje sekwencję Boost MPL zawierającą te klasy. Następnie napisałem klasę, która próbuje użyć wskaźnika do AbstractPlugin, jeśli można go rzutować na dowolny typ w tej sekwencji MPL. Problem polega na tym, że moja funkcja preprocesora wymaga pełnej listy wszystkich rozszerzeń, które chcę utworzyć i załadować. Czy jest jakaś technika, która pozwala mi zarejestrować każde rozszerzenie w osobnym pliku?Zarejestruj klasę C++, aby później funkcja mogła wykonywać iteracje dla wszystkich zarejestrowanych klas.

Aktualizacja:

wierzę, moje wyjaśnienie sytuacji było zbyt niejasne, więc postanowiłem zrobić to dokładniej.

Chciałbym zdefiniować kolekcję typów rozszerzeń. Dla każdego typu rozszerzenia może istnieć dowolna liczba rozszerzeń. W czasie działania program ładuje bibliotekę zewnętrzną, rozpoznaje funkcję punktu wejścia, wywołuje ją, aw rezultacie otrzymuje wskaźnik. Następnie próbuje rzucić ten wskaźnik do wszystkich zarejestrowanych typów rozszerzeń (używając dynamic_cast, więc klasy dla typów rozszerzeń dziedziczą z pewnej polimorficznej klasy bazowej). Jeśli rzut do jakiegoś typu rozszerzenia powiedzie się, rzutowany wskaźnik jest używany w wywołaniu specjalnego programu obsługi dla tego typu rozszerzenia.

Liczba typów rozszerzeń jest znana w czasie kompilacji (podczas gdy oczywiście liczba rozszerzeń jest nieskończona). Za pomocą mojego aproach klasa loader używa tej wiedzy do sprawdzenia, czy istnieje program obsługi dla każdego typu rozszerzenia (jeśli nie, program się nie kompiluje). Ponadto, moje podejście nie wymusza na klasach typów rozszerzeń żadnych informacji o ładowaczu (więc łatwo jest modyfikować program ładujący). Ale byłoby wygodniej, gdyby każdy typ rozszerzenia się zarejestrował.

+0

Czy wygenerowanie nagłówka jest akceptowalnym rozwiązaniem? – Arpegius

Odpowiedz

0

Okazuje się, że to, czego chcę, jest niemożliwe. Powodem tego jest "rejestracja" w tym kontekście oznacza "wstaw typ do typu sekwencji", a sekwencje typu są niezmienne, ponieważ same są typami. Tak więc należy albo utworzyć tę sekwencję ręcznie, albo jak niektórzy sugerują przenieść "rejestrację" do środowiska wykonawczego.

0

Nie jest trudno wdrożyć taką strukturę rozszerzeń przy użyciu abstrakcyjnego wzoru fabrycznego.

http://en.wikipedia.org/wiki/Abstract_factory_pattern

Można zarejestrować te funkcje fabryka/abstrakcyjnych obiektów w wykazie globalnym, i robić, co chcesz zrobić bazę na nim.

8

Możesz dokonać samodzielnej rejestracji wszystkich zajęć w jakiejś kolekcji. Oto podejście szkielet:

Base.hpp:

#include <memory> 
#include <unordered_map> 
#include <string> 

struct Base 
{ 
    virtual ~Base() = default; 

    using create_f = std::unique_ptr<Base>(); 

    static void registrate(std::string const & name, create_f * fp) 
    { 
     registry()[name] = fp; 
    } 

    static std::unique_ptr<Base> instantiate(std::string const & name) 
    { 
     auto it = registry().find(name); 
     return it == registry().end() ? nullptr : (it->second)(); 
    } 

    template <typename D> 
    struct Registrar 
    { 
     explicit Registrar(std::string const & name) 
     { 
      Base::registrate(name, &D::create); 
     } 
     // make non-copyable, etc. 
    }; 

private: 
    static std::unordered_map<std::string, create_f *> & registry(); 
}; 

Base.cpp:

#include "Base.hpp" 

std::unordered_map<std::string, Base::create_f *> & Base::registry() 
{ 
    static std::unordered_map<std::string, Base::create_f *> impl; 
    return impl; 
} 

Teraz do tego użyć w kliencie:

pochodnych. hpp:

#include "Base.hpp" 

struct Derived : Base 
{ 
    static std::unique_ptr<Base> create() { return std::make_unique<Derived>(); } 
    // ... 
}; 

Derived.cpp:

#include "Derived.hpp" 

namespace 
{ 
    Base::Registrar<Derived> registrar("MyClass"); 
} 

Konstruktor z Base::Registrar<Derived> dba o rejestracji klasę Derived pod nazwą "MyClass".Można tworzyć instancje Derived dynamicznie poprzez:

std::unique_ptr<Base> p = Base::instantiate("MyClass"); 

Kod mogłyby/powinny zostać ulepszone poprzez wykrywanie powtarzania rejestracje, drukując listę dostępnych klas, itd. Zauważ, jak uniknąć problemów statycznych inicjalizacji zamawiania mój rozpoczęciem właściwego obiekt mapy rejestru obiekt blokowo-statyczny, który gwarantuje, że zostanie zainicjowany przed pierwszym użyciem, a tym samym zniszczony dopiero po ostatnim użyciu.

+0

Rozważałem coś takiego, ale bardziej interesuje mnie podejście do kompilacji. Nie jestem pewien, czy istnieje, ale wciąż, kraina szablonów C++ jest pełna cudów. – grisumbras

+0

Nie ma statycznego. Nie można uczynić klas rejestrować samodzielnie, ponieważ każdy plik implementacji ma własną jednostkę kompilacji, a każda z nich może być umieszczona w innej bibliotece. Siła tego rozwiązania polega na tym, że po załadowaniu biblioteki dynamicznej klasa rozszerzenia stała się dostępna automagicznie. Jest to dobrze znany i bardzo przydatny wzór. – Arpegius

+0

To bardzo ładne wdrożenie. Jedyne, czego nie rozumiem, to = 0 w statycznej funkcji create(). O ile mi wiadomo, jest to nieprawidłowe C++, ponieważ funkcje statyczne nigdy nie mogą być wirtualne. Jak ta część powinna działać? – Shenjoku

Powiązane problemy