Mercurial > code
changeset 592:2bd11ea63822
Dynlib: move to bitbucket
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 31 Aug 2016 18:48:03 +0200 |
parents | 3547fc5afba2 |
children | c767f1202e8e |
files | CMakeLists.txt modules/dynlib/CMakeLists.txt modules/dynlib/doc/mainpage.cpp modules/dynlib/dynlib.hpp modules/dynlib/test/main.cpp modules/dynlib/test/plugin.cpp |
diffstat | 6 files changed, 0 insertions(+), 509 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Wed Aug 31 18:35:39 2016 +0200 +++ b/CMakeLists.txt Wed Aug 31 18:48:03 2016 +0200 @@ -49,7 +49,6 @@ ) endif () -add_subdirectory(modules/dynlib) add_subdirectory(modules/elapsed-timer) add_subdirectory(modules/fs) add_subdirectory(modules/hash)
--- a/modules/dynlib/CMakeLists.txt Wed Aug 31 18:35:39 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -# -# CMakeLists.txt -- code building for common code -# -# Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -add_library(dynlib-plugin MODULE ${CMAKE_CURRENT_SOURCE_DIR}/test/plugin.cpp) -target_include_directories(dynlib-plugin PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - -code_define_module( - NAME dynlib - SOURCES dynlib.hpp - LIBRARIES $<$<STREQUAL:${CMAKE_SYSTEM_NAME},Linux>:dl> - FLAGS PLUGIN=\"$<TARGET_FILE:dynlib-plugin>\" -) - -add_dependencies(test-dynlib dynlib-plugin)
--- a/modules/dynlib/doc/mainpage.cpp Wed Aug 31 18:35:39 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/** - * \mainpage - * - * Welcome to the dynlib library. - * - * ## Introduction - * - * Opening shared libraries from C++ (e.g for plugins) is platform specific, with that class, you can open them very - * easily. - * - * ## Installation - * - * Just copy the file dynlib.hpp and add it to your project. - * - * ## Overview - * - * This one minute tutorial shows how to load a shared library named `plugin.so` and execute a C function `say_hello`. - * - * ### Main - * - * This is the main executable that loads the `plugin.so` file. - * - * ````cpp - * #include <iostream> - * - * #include "dynlib.hpp" - * - * using HelloFunc = void (*)(const std::string &); - * - * int main() - * { - * try { - * Dynlib library("./plugin.so"); - * - * HelloFunc hello = library.sym<HelloFunc>("say_hello"); - * hello("Test"); - * } catch (const std::exception &error) { - * std::cerr << error.what() << std::endl; - * } - * - * return 0; - * } - * ```` - * - * ### Plugin - * - * This is the plugin that you compile as a shared library. - * - * For example with gcc: `gcc -fPIC -shared plugin.cpp -o plugin.so`. - * - * ````cpp - * #include <iostream> - * - * #include "dynlib.hpp" - * - * extern "C" { - * - * DYNLIB_EXPORT void say_hello() - * { - * std::cout << "Hello from the plugin!" << std::endl; - * } - * ```` - */
--- a/modules/dynlib/dynlib.hpp Wed Aug 31 18:35:39 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,346 +0,0 @@ -/* - * dynlib.hpp -- portable shared library loader - * - * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef DYNLIB_HPP -#define DYNLIB_HPP - -/** - * \file dynlib.hpp - * \brief Portable shared library loader. - * \author David Demelier <markand@malikania.fr> - */ - -/** - * \page Dynlib Dynlib - * \brief Portable shared library loader. - * - * The dynlib module let you open shared libraries dynamically at runtime. - * - * ## Operating system support - * - * | System | Support | Remarks | - * |---------|---------|--------------------| - * | Apple | Ok | | - * | FreeBSD | Ok | | - * | Linux | Ok | Needs -ldl library | - * | Windows | Ok | | - * - * ## How to export symbols - * - * When you want to dynamically load symbols from your shared library, make - * sure they are in a `extern "C"` block, if not they will be - * [mangled][name-mangling]. - * - * Note, this does not mean that you can't write C++ code, it just mean that - * you can't use namespaces and function overloading. - * - * Example of **plugin.cpp**: - * - * ````cpp - * #include <iostream> - * - * #include "dynlib.hpp" - * - * extern "C" { - * - * DYNLIB_EXPORT void plugin_load() - * { - * std::cout << "Loading plugin" << std::endl; - * } - * - * DYNLIB_EXPORT void plugin_unload() - * { - * std::cout << "Unloading plugin" << std::endl; - * } - * - * } - * ```` - * - * The \ref DYNLIB_EXPORT macro is necessary on some platforms to be sure that - * symbols will be visible. Make sure you always add it before any function. - * - * To compile, see your compiler documentation or build system. For gcc you can - * use the following: - * - * ```` - * gcc -std=c++14 -shared plugin.cpp -o plugin.so - * ```` - * - * ## How to load the library - * - * The dynlib module will search for the library in various places, thus you - * can use relative paths names but be sure that the library can be found. - * Otherwise, just use an absolute path to the file. - * - * ````cpp - * #include <iostream> - * - * #include "dynlib.hpp" - * - * int main() - * { - * try { - * Dynlib dso("./plugin" DYNLIB_SUFFIX); - * } catch (const std::exception &ex) { - * std::cerr << ex.what() << std::endl; - * } - * - * return 0; - * } - * ```` - * - * ## How to load symbol - * - * The last part is symbol loading, you muse use raw C function pointer and - * the Dynlib::sym function. - * - * ````cpp - * #include <iostream> - * - * #include "dynlib.hpp" - * - * using PluginLoad = void (*)(); - * using PluginUnload = void (*)(); - * - * int main() - * { - * try { - * Dynlib dso("./plugin" DYNLIB_SUFFIX); - * - * dso.require<PluginLoad>("plugin_load")(); - * dso.require<PluginUnload>("plugin_unload")(); - * } catch (const std::exception &ex) { - * std::cerr << ex.what() << std::endl; - * } - * - * return 0; - * } - * ```` - * - * [name-mangling]: https://en.wikipedia.org/wiki/Name_mangling - */ - -#include <stdexcept> -#include <string> - -#if defined(_WIN32) -# include <Windows.h> -#else -# include <dlfcn.h> -#endif - -/** - * \brief Export the symbol. - * - * This is required on some platforms and you should put it before your - * function signature. - * - * \code{.cpp} - * extern "C" { - * - * DYNLIB_EXPORT void my_function() - * { - * } - * - * } - * \endcode - */ -#if defined(_WIN32) -# define DYNLIB_EXPORT __declspec(dllexport) -#else -# define DYNLIB_EXPORT -#endif - -/** - * \brief Usual suffix for the library. - * - * This macro expands to the suffix convention for this platform. - * - * \code{.cpp} - * Dynlib library("./myplugin" DYNLIB_SUFFIX); - * \endcode - * - * \note Don't use the suffix expanded value shown in Doxygen as it may be - * wrong. - */ -#if defined(_WIN32) -# define DYNLIB_SUFFIX ".dll" -#elif defined(__APPLE__) -# define DYNLIB_SUFFIX ".dylib" -#else -# define DYNLIB_SUFFIX ".so" -#endif - -/** - * \class Dynlib - * \brief Load a dynamic module. - * - * This class is a portable wrapper to load shared libraries on supported - * systems. - */ -class Dynlib { -private: -#if defined(_WIN32) - using Handle = HMODULE; - using Sym = FARPROC; -#else - using Handle = void *; - using Sym = void *; -#endif - -public: - /** - * \brief Policy for symbol resolution. - */ - enum Policy { - Immediately, //!< load symbols immediately - Lazy //!< load symbols when needed - }; - -private: - Handle m_handle; - - Dynlib(const Dynlib &) = delete; - Dynlib &operator=(const Dynlib &) = delete; - - Dynlib(Dynlib &&) = delete; - Dynlib &operator=(Dynlib &&) = delete; - -#if defined(_WIN32) - std::string error() - { - LPSTR error = nullptr; - std::string errmsg; - - FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&error, 0, NULL); - - if (error) { - errmsg = std::string(error); - LocalFree(error); - } - - return errmsg; - } -#endif - -public: - /** - * Constructor to load a shared module. - * - * \param path the absolute path - * \param policy the policy to load - * \throw std::runtime_error on error - */ - Dynlib(const std::string &path, Policy policy = Immediately); - - /** - * Close the library automatically. - */ - ~Dynlib(); - - /** - * Get a symbol from the library. - * - * \param name the symbol - * \return the symbol - * \throw std::out_of_range on error - * \see DYNLIB_EXPORT - */ - template <typename T> - inline T require(const std::string &name) - { - auto sym = get<T>(name); - - if (!sym) - throw std::out_of_range(name + ": symbol not found"); - - return sym; - } - - /** - * Get a symbol from the library or return nullptr if not found. - * - * \param name the symbol - * \return the symbol - */ - template <typename T> - T get(const std::string &name) noexcept; -}; - -#if defined(_WIN32) - -/* - * Windows implementation - * ------------------------------------------------------------------ - */ - -inline Dynlib::Dynlib(const std::string &path, Policy) -{ - m_handle = LoadLibraryA(path.c_str()); - - if (m_handle == nullptr) - throw std::runtime_error(error()); -} - -inline Dynlib::~Dynlib() -{ - FreeLibrary(m_handle); - m_handle = nullptr; -} - -template <typename T> -inline T Dynlib::get(const std::string &name) noexcept -{ - return reinterpret_cast<T>(GetProcAddress(m_handle, name.c_str())); -} - -#else - -/* - * Unix implementation - * ------------------------------------------------------------------ - */ - -inline Dynlib::Dynlib(const std::string &path, Policy policy) -{ - m_handle = dlopen(path.c_str(), policy == Immediately ? RTLD_NOW : RTLD_LAZY); - - if (m_handle == nullptr) - throw std::runtime_error(dlerror()); -} - -inline Dynlib::~Dynlib() -{ - dlclose(m_handle); - m_handle = nullptr; -} - -template <typename T> -inline T Dynlib::get(const std::string &name) noexcept -{ - return reinterpret_cast<T>(dlsym(m_handle, name.c_str())); -} - -#endif - -#endif // !DYNLIB_HPP
--- a/modules/dynlib/test/main.cpp Wed Aug 31 18:35:39 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* - * main.cpp -- test the dynamic library loader - * - * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <iostream> - -#include <gtest/gtest.h> - -#include <dynlib.hpp> - -using Initialize = void (*)(std::string &s); - -TEST(Basic, initialize) -{ - try { - Dynlib library(PLUGIN); - Initialize init = library.require<Initialize>("initialize"); - - std::string expected("Hello World"); - std::string result; - - init(result); - - ASSERT_EQ(expected, result); - ASSERT_TRUE(library.get<Initialize>("initialize")); - } catch (const std::runtime_error &error) { - FAIL() << error.what(); - } -} - -TEST(Basic, absent) -{ - Dynlib library(PLUGIN); - - ASSERT_THROW(library.require<Initialize>("initialize_typo"), std::out_of_range); - ASSERT_FALSE(library.get<Initialize>("initialize_type")); -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}