boost::function beim ersten Aufruf erst zuweisen
-
Ich möchte mir das Linken während der Laufzeit gegen eine dynamische Bibliothek etwas vereinfachen und hab mir dafür einen kleinen Wrapper geschrieben:
template<class T> class WeakFunction : public boost::function<T> { public: WeakFunction(const char* const name, const char* const libName) : name_(name), libName_(libName) { } void Load() { static void* handle = dlopen(libName_, RTLD_LAZY); if(!handle) { throw WeakLinkingError(std::string("Could not open ") + libName_ + "."); } static_cast<boost::function<T>*>(this)->operator=(reinterpret_cast<T*>(dlsym(handle, name_))); if(!*this) { throw WeakLinkingError(std::string("Could not find ") + name_ + " in " + libName_ + "."); } } private: const char* const name_; const char* const libName_; }; // Beispiel: WeakFunction<void(ALsizei, ALuint*)> alGenBuffers("alGenBuffers", "OpenAL32.dll");Das Problem ist jetzt, dass ich, bevor die Funktion aufgerufen wird, einmal die Load funktion aufrufen muss. Besser wäre es natürlich, wenn die Funktion dies automatisch tut, wenn sie das erste Mal aufgerufen wird. Ist das überhaupt möglich? Wenn ja, wie?
-
joomoo schrieb:
Das Problem ist jetzt, dass ich, bevor die Funktion aufgerufen wird, einmal die Load funktion aufrufen muss. Besser wäre es natürlich, wenn die Funktion dies automatisch tut, wenn sie das erste Mal aufgerufen wird. Ist das überhaupt möglich? Wenn ja, wie?
operator() definieren der beim ersten call dlopen macht und nachher dann eben an function<T>::operator() weiter gibt.
uU ist es nötig dafür eine Manager Klasse zu schreiben die mehrfaches dlopen abfängt.
die signatur des operator() kannst du dir von function<T> ja geben lassen...
PS:
function objekte werden nicht polymorph verwendet, deshalb ist ein verdecken des operator() kein problem.
-
Kannst du dich auf wenige Funktionssignaturen beschränken oder willst du diesbezüglich generisch bleiben? Mir fällt nämlich nur gerade das Überschreiben des
operator()ein (wenn notwendig initialisieren und dann Basisklassenfunktion aufrufen). Mir scheint, zu dem Zweck wäre auch Aggregation besser als Vererbung geeignet.Initialisierung im Konstruktor ist keine Möglichkeit?
-
Shade Of Mine schrieb:
operator() definieren der beim ersten call dlopen macht und nachher dann eben an function<T>::operator() weiter gibt.
Wie löse ich das Problem, dass ich ja nicht weiß, wieviele Argumente operator() hat?
Nexus schrieb:
Initialisierung im Konstruktor ist keine Möglichkeit?
Leider nicht, denn die Funktionen werden Global erstellt und dann würde es ja schon beim Programmstart eine Exception geben, die ich nicht abfangen kann.
-
joomoo schrieb:
Wie löse ich das Problem, dass ich ja nicht weiß, wieviele Argumente operator() hat?
Mit momentanem C++-Standard sehe ich ausser dem Überladen etlicher Funktionen keine Möglichkeit, deshalb auch die Frage, ob du generisch bleiben müsstest. Der notwendige Code kann mit Präprozessortricks (Boost.Preprocessor) zwar klein gehalten werden, aber im Grunde genommen ist das immer noch nicht sehr schön.
In C++0x wirds Variadic Templates geben.
-
joomoo schrieb:
Wie löse ich das Problem, dass ich ja nicht weiß, wieviele Argumente operator() hat?
AH, ich dachte function liefert das irgendwie leicht - aber das geht ja nicht. Mein Fehler.
Ist n bisschen haesslich, aber solange es keine variadic templates gibt ist das ja immer so

WeakFunction erbt von WeakFunctionHelper<function<T>::arity, function<T>>
dann musst du halt für 0-N argumente spezialisieren...
-
Das einzige was ich mir jetzt noch vorstellen könnte wäre so etwas:
template<class T> class WeakFunction { public: WeakFunction(const char* const name, const char* const libName) : name_(name), libName_(libName), function_() { } void Load() { static void* handle = dlopen(libName_, RTLD_LAZY); if(!handle) { throw WeakLinkingError(std::string("Could not open ") + libName_ + "."); } function_ = reinterpret_cast<T*>(dlsym(handle, name_)); if(!function_) { throw WeakLinkingError(std::string("Could not find ") + name_ + " in " + libName_ + "."); } } boost::function<T> & operator(){ if( !function_ ){ Load(); } return function_; } private: const char* const name_; const char* const libName_; boost::function<T> function_; };Damit müsstest du dann so aufrufen:
WeakFunction<void(ALsizei, ALuint*)> alGenBuffers("alGenBuffers", "OpenAL32.dll"); alGenBuffers()(0, 0);Edit: // Some Logic fix
-
Also Variadic Templates sind kein Problem, benutze GCC 4.4. Ich bin jetzt so weit gekommen:
std::map<std::string, void*> handles_; template<typename ReturnType, typename... Args> class WeakFunction { public: WeakFunction(const char* const name, const char* const libName) : name_(name), libName_(libName) { } ReturnType operator()(Args... args) { static bool loaded = false; if(!loaded) { Load(); } return function_(args...); } void Load() { decltype(handles_.end()) i; if((i = handles_.find(libName_)) == handles_.end()) // library hasn't been loaded yet? { static void* handle = dlopen(libName_.c_str(), RTLD_LAZY); if(!handle) { throw WeakLinkingError(std::string("Could not open ") + libName_ + "."); } i->second = handle; } function_ = reinterpret_cast<boost::add_pointer<ReturnType(Args...)>::type>(dlsym(i->second, name_.c_str())); // Problem if(!function_) { throw WeakLinkingError(std::string("Could not find ") + name_ + " in " + libName_ + "."); } } private: boost::function<ReturnType(Args...)> function_; const std::string name_; const std::string libName_; };Das Problem liegt in der mit "// Problem" markierten Zeile.
55|error: expected type-specifier| 55|error: expected '>'| 55|error: expected '('| 55|error: expected ')' before ';' token|Wenn ich das mit add_pointer weglasse geht's, nur dann ist es ein ungültiger cast. Ich krieg das nicht gecastet, hab auch schon typdefs oder ein simples * versucht - ohne Erfolg. Jemand nen Tipp? Sieht für mich nach nem Bug in GCC aus.
-
dlopen/dlclose unterhalten selbst einen Referenzzähler auf Bibliotheken, die Handle-Map sollte also nicht notwendig sein.
Ich finge das etwa so an:
#include <functional> #include <iostream> #include <stdexcept> #include <string> #include <dlfcn.h> #include <unistd.h> struct link_error : public std::invalid_argument { link_error() : std::invalid_argument(dlerror()) { } }; class dll_handle { public: dll_handle(std::string const &dll_name) : name_ (dll_name), handle_(0) { } dll_handle(dll_handle const &rhs) : name_(rhs.name_), handle_(0) { if(rhs.handle_) { open(); } } ~dll_handle() { try { close(); } catch(link_error const &e) { std::cerr << e.what() << std::endl; } } void open() { if(!handle_) { handle_ = dlopen(name_.c_str(), RTLD_LAZY); if(!handle_) { throw link_error(); } } } void close() { if(handle_ && dlclose(handle_) != 0) { throw link_error(); } } void *symbol(std::string const &symbol_name) { open(); void *sym = dlsym(handle_, symbol_name.c_str()); if(!sym) { throw link_error(); } return sym; } private: std::string name_; void *handle_; }; template<typename T> class weak_function; template<typename R, typename... A> class weak_function<R(A...)> { public: weak_function(std::string const &dll, std::string const &symbol) : dll_ (dll), symbol_name_(symbol), fo_ () { } void link_symbol() { if(!fo_) { R (*sym)(A...); *reinterpret_cast<void**>(&sym) = dll_.symbol(symbol_name_); fo_ = sym; } } R operator()(A... a) { link_symbol(); return fo_.operator()(a...); } private: dll_handle dll_; std::string symbol_name_; std::function<R(A...)> fo_; }; int main() { weak_function<pid_t(void)> wf("/lib/libc.so.6", "getpid"); weak_function<pid_t(void)> wf2("/lib/libc.so.6", "foobarbazqux"); std::cout << wf() << std::endl; std::cout << getpid() << std::endl; wf2(); }Allerdings ist das eine ziemlich grobe Angelegenheit. Ein großes Designproblem ist 1. Kopiersemantik und 2. const-correctness. Wenn ich etwa eine Funktion der Form
void call(weak_function<void()> const &func) { func(); }habe, wie soll sich func verhalten, wenn die Funktion noch nicht geladen ist? Wahrscheinlich könnte man die Brechstangenmethode (mutable) in diesem Fall sogar ganz gut vertreten, allerdings kommt das auf die weiteren Umstände an.