#pragma once #include #include namespace fs = std::experimental::filesystem; #include #include #include #include "utils/exceptions.hpp" namespace utils { /** * @brief Exception raised by @c DynamicLib. */ class DynamicLibException : public utils::BasicException { public: using utils::BasicException::BasicException; }; /** * DynamicLib is a wrapper aroung dynamic object returned by dlopen. * * Dynamic object must have extern C functions which names should be * "produce" and "destruct" (that's by convention). * * The functions prototypes can be defined with template parameter * type trait (T). * * DynamicLib isn't implemented for concurrent access. * * @tparam T type trait which defines the prototypes of extern C functions * of undelying dynamic object. */ template class DynamicLib { public: /** * Initializes dynamic library (loads lib, produce and * destruct functions) * * @param lib_path file system path to dynamic library */ DynamicLib(const fs::path &lib_path) : lib_path(lib_path), lib_object(nullptr) { // load dynamic lib // I've added the RTL_DEEPBIND flag when we are opening the dynamic_lib to // resolve symbols locally instead of globally. For additional information // take a look at: http://man7.org/linux/man-pages/man3/dlopen.3.html dynamic_lib = dlopen(lib_path.c_str(), RTLD_NOW | RTLD_DEEPBIND); if (!dynamic_lib) throw DynamicLibException(dlerror()); dlerror(); /* Clear any existing error */ DLOG(INFO) << "dynamic lib at ADDRESS " << dynamic_lib << " was opened"; // load produce method this->produce_method = (typename T::ProducePrototype)dlsym(dynamic_lib, "produce"); const char *dlsym_produce_error = dlerror(); if (dlsym_produce_error) throw DynamicLibException(dlsym_produce_error); // load destruct method this->destruct_method = (typename T::DestructPrototype)dlsym(dynamic_lib, "destruct"); const char *dlsym_destruct_error = dlerror(); if (dlsym_destruct_error) throw DynamicLibException(dlsym_destruct_error); } // becuase we are dealing with pointers // and conceptualy there is no need to copy the instance of this class // the copy constructor and copy assignment operator are deleted // the same applies to move methods DynamicLib(const DynamicLib &other) = delete; DynamicLib &operator=(const DynamicLib &other) = delete; DynamicLib(DynamicLib &&other) = delete; DynamicLib &operator=(DynamicLib &&other) = delete; /** * Singleton method. Returns the same instance of underlying class * for every call. The instance of underlying class is returned by * extern C produce function. * * @return T the instance of lib class */ typename T::ObjectPrototype *instance() { if (lib_object == nullptr) lib_object = this->produce_method(); return lib_object; } /** * Clean underlying object and close the lib */ ~DynamicLib() { // first destroy underlying object if (lib_object != nullptr) { DLOG(INFO) << "shared object at ADDRESS " << (void *)lib_object << " will be destroyed."; this->destruct_method(lib_object); } // then destroy dynamic lib DLOG(INFO) << "unloading lib " << lib_path.c_str(); if (dynamic_lib != nullptr) { DLOG(INFO) << "closing dynamic lib ADDRESS " << (void *)dynamic_lib; // // IMPORTANT: weird problem the program SEGFAULT on dlclose // // TODO: FIX somehow // // maybe something similar is: // // // http://stackoverflow.com/questions/6450828/segmentation-fault-when-using-dlclose-on-android-platform // // for now it is not crucial so I've created a task for that // // ! 0 is success // Return early because dlclose seems to be casuing the problem again. So // strange. return; int closing_status = dlclose(dynamic_lib); if (closing_status != 0) throw DynamicLibException(dlerror()); } else { DLOG(WARNING) << "unload lib was called but lib ptr is null"; } } private: std::string lib_path; void *dynamic_lib; typename T::ObjectPrototype *lib_object; typename T::ProducePrototype produce_method; typename T::DestructPrototype destruct_method; }; } // namespace utils