diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-09-18 02:35:30 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-09-18 02:35:30 +0100 |
commit | b99a435f52de826327ea3e4cc8cc8aaf348d5b53 (patch) | |
tree | 90fd3bf144108aa0a9d27856b3f5eb65ab6d0020 | |
parent | Support addition and removal of plugin resolver functions (diff) | |
download | libadhocutil-b99a435f52de826327ea3e4cc8cc8aaf348d5b53.tar.bz2 libadhocutil-b99a435f52de826327ea3e4cc8cc8aaf348d5b53.tar.xz libadhocutil-b99a435f52de826327ea3e4cc8cc8aaf348d5b53.zip |
Use resolvers to load libraries automagically on demand
-rw-r--r-- | libadhocutil/Jamfile.jam | 2 | ||||
-rw-r--r-- | libadhocutil/plugins.cpp | 24 | ||||
-rw-r--r-- | libadhocutil/plugins.h | 9 | ||||
-rw-r--r-- | libadhocutil/unittests/testPluginsRuntime.cpp | 50 |
4 files changed, 80 insertions, 5 deletions
diff --git a/libadhocutil/Jamfile.jam b/libadhocutil/Jamfile.jam index 45bbeec..861509a 100644 --- a/libadhocutil/Jamfile.jam +++ b/libadhocutil/Jamfile.jam @@ -6,6 +6,7 @@ lib Ice : ; lib IceUtil ; lib pthread ; lib curl ; +lib dl ; alias iceall : : : : <library>Ice <library>IceUtil <library>pthread ; @@ -15,6 +16,7 @@ lib adhocutil : <include>. <library>iceall <library>curl + <library>dl <cflags>-fvisibility=hidden <variant>release:<cflags>-flto : : diff --git a/libadhocutil/plugins.cpp b/libadhocutil/plugins.cpp index 6caf34f..a1eef70 100644 --- a/libadhocutil/plugins.cpp +++ b/libadhocutil/plugins.cpp @@ -1,5 +1,6 @@ #include "plugins.h" #include <string.h> +#include <dlfcn.h> #include <boost/multi_index_container.hpp> #include <boost/multi_index/ordered_index.hpp> #include "buffer.h" @@ -73,6 +74,11 @@ namespace AdHoc { { } + LoadLibraryException::LoadLibraryException(const std::string & f, const char * msg) : + std::runtime_error(stringbf("Failed to load library [%s]; %s", f, msg)) + { + } + PluginManager::PluginManager() : plugins(new PluginStore()), resolvers(new TypePluginResolvers()) @@ -111,12 +117,30 @@ namespace AdHoc { PluginManager::get(const std::string & n, const std::type_info & t) const { auto r = plugins->get<2>().equal_range(boost::make_tuple(n, std::cref(t))); + if (r.first == r.second) { + auto tr = resolvers->find(t.hash_code()); + if (tr != resolvers->end()) { + if (auto lib = tr->second(t, n)) { + loadLibrary(*lib); + } + } + r = plugins->get<2>().equal_range(boost::make_tuple(n, std::cref(t))); + } if (r.first != r.second) { return (*r.first); } throw NoSuchPluginException(n, t); } + void + PluginManager::loadLibrary(const std::string & f) + { + void * handle = dlopen(f.c_str(), RTLD_NOW); + if (!handle) { + throw LoadLibraryException(f, dlerror()); + } + } + std::set<PluginPtr> PluginManager::getAll() const { diff --git a/libadhocutil/plugins.h b/libadhocutil/plugins.h index 8b8efac..03d59fb 100644 --- a/libadhocutil/plugins.h +++ b/libadhocutil/plugins.h @@ -63,6 +63,13 @@ namespace AdHoc { DuplicateResolverException(const std::type_info &); }; + /// Thrown when an attempt to load a library fails. + class LoadLibraryException : public std::runtime_error { + public: + /// Constuctor taking syscall error details. + LoadLibraryException(const std::string & f, const char * msg); + }; + template <typename T> /// Typed plugin and handle to implementation. class DLL_PUBLIC PluginOf : public Plugin { @@ -171,6 +178,8 @@ namespace AdHoc { static PluginManager * getDefault(); private: + static void loadLibrary(const std::string &); + typedef boost::multi_index_container<PluginPtr, boost::multi_index::indexed_by< boost::multi_index::ordered_non_unique<boost::multi_index::member<Plugin, const std::string, &Plugin::name>>, diff --git a/libadhocutil/unittests/testPluginsRuntime.cpp b/libadhocutil/unittests/testPluginsRuntime.cpp index 2b13913..aa1dcf1 100644 --- a/libadhocutil/unittests/testPluginsRuntime.cpp +++ b/libadhocutil/unittests/testPluginsRuntime.cpp @@ -13,7 +13,9 @@ auto variant = selfExe.parent_path().leaf(); auto toolset = selfExe.parent_path().parent_path().leaf(); auto lib = rootDir / "bin" / toolset / variant / "libutilTestClasses.so"; -static boost::optional<std::string> resolver(const std::type_info &, const std::string &); +static boost::optional<std::string> nullResolver(const std::type_info &, const std::string &); +static boost::optional<std::string> badResolver(const std::type_info &, const std::string &); +static boost::optional<std::string> goodResolver(const std::type_info &, const std::string &); BOOST_AUTO_TEST_CASE( ready ) { @@ -36,20 +38,58 @@ BOOST_AUTO_TEST_CASE( loadAndUnloadlib ) } boost::optional<std::string> -resolver(const std::type_info &, const std::string &) +nullResolver(const std::type_info &, const std::string &) { - return std::string(); + return nullptr; +} + +boost::optional<std::string> +badResolver(const std::type_info &, const std::string &) +{ + return std::string("dontexist"); +} + +boost::optional<std::string> +goodResolver(const std::type_info & t, const std::string & n) +{ + BOOST_REQUIRE_EQUAL(typeid(BaseThing), t); + BOOST_REQUIRE_EQUAL("ImplOfThing", n); + return lib.string(); } BOOST_AUTO_TEST_CASE( addAndRemoveResolver ) { auto pm = AdHoc::PluginManager::getDefault(); BOOST_REQUIRE_EQUAL(0, pm->countResolvers()); - pm->addResolver<BaseThing>(resolver); + pm->addResolver<BaseThing>(nullResolver); BOOST_REQUIRE_EQUAL(1, pm->countResolvers()); - BOOST_REQUIRE_THROW(pm->addResolver<BaseThing>(resolver), DuplicateResolverException); + BOOST_REQUIRE_THROW(pm->addResolver<BaseThing>(nullResolver), DuplicateResolverException); BOOST_REQUIRE_EQUAL(1, pm->countResolvers()); pm->removeResolver<BaseThing>(); BOOST_REQUIRE_EQUAL(0, pm->countResolvers()); } +BOOST_AUTO_TEST_CASE( null ) +{ + auto pm = AdHoc::PluginManager::getDefault(); + pm->addResolver<BaseThing>(nullResolver); + BOOST_REQUIRE_THROW(pm->get<BaseThing>("ImplOfThing"), AdHoc::NoSuchPluginException); + pm->removeResolver<BaseThing>(); +} + +BOOST_AUTO_TEST_CASE( bad ) +{ + auto pm = AdHoc::PluginManager::getDefault(); + pm->addResolver<BaseThing>(badResolver); + BOOST_REQUIRE_THROW(pm->get<BaseThing>("ImplOfThing"), AdHoc::LoadLibraryException); + pm->removeResolver<BaseThing>(); +} + +BOOST_AUTO_TEST_CASE( good ) +{ + auto pm = AdHoc::PluginManager::getDefault(); + pm->addResolver<BaseThing>(goodResolver); + BOOST_REQUIRE(pm->get<BaseThing>("ImplOfThing")); + pm->removeResolver<BaseThing>(); +} + |