From b99a435f52de826327ea3e4cc8cc8aaf348d5b53 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 18 Sep 2015 02:35:30 +0100 Subject: Use resolvers to load libraries automagically on demand --- libadhocutil/Jamfile.jam | 2 ++ libadhocutil/plugins.cpp | 24 +++++++++++++ libadhocutil/plugins.h | 9 +++++ 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 : : : : Ice IceUtil pthread ; @@ -15,6 +16,7 @@ lib adhocutil : . iceall curl + dl -fvisibility=hidden release:-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 +#include #include #include #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 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 /// 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>, 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 resolver(const std::type_info &, const std::string &); +static boost::optional nullResolver(const std::type_info &, const std::string &); +static boost::optional badResolver(const std::type_info &, const std::string &); +static boost::optional goodResolver(const std::type_info &, const std::string &); BOOST_AUTO_TEST_CASE( ready ) { @@ -36,20 +38,58 @@ BOOST_AUTO_TEST_CASE( loadAndUnloadlib ) } boost::optional -resolver(const std::type_info &, const std::string &) +nullResolver(const std::type_info &, const std::string &) { - return std::string(); + return nullptr; +} + +boost::optional +badResolver(const std::type_info &, const std::string &) +{ + return std::string("dontexist"); +} + +boost::optional +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(resolver); + pm->addResolver(nullResolver); BOOST_REQUIRE_EQUAL(1, pm->countResolvers()); - BOOST_REQUIRE_THROW(pm->addResolver(resolver), DuplicateResolverException); + BOOST_REQUIRE_THROW(pm->addResolver(nullResolver), DuplicateResolverException); BOOST_REQUIRE_EQUAL(1, pm->countResolvers()); pm->removeResolver(); BOOST_REQUIRE_EQUAL(0, pm->countResolvers()); } +BOOST_AUTO_TEST_CASE( null ) +{ + auto pm = AdHoc::PluginManager::getDefault(); + pm->addResolver(nullResolver); + BOOST_REQUIRE_THROW(pm->get("ImplOfThing"), AdHoc::NoSuchPluginException); + pm->removeResolver(); +} + +BOOST_AUTO_TEST_CASE( bad ) +{ + auto pm = AdHoc::PluginManager::getDefault(); + pm->addResolver(badResolver); + BOOST_REQUIRE_THROW(pm->get("ImplOfThing"), AdHoc::LoadLibraryException); + pm->removeResolver(); +} + +BOOST_AUTO_TEST_CASE( good ) +{ + auto pm = AdHoc::PluginManager::getDefault(); + pm->addResolver(goodResolver); + BOOST_REQUIRE(pm->get("ImplOfThing")); + pm->removeResolver(); +} + -- cgit v1.2.3