-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add mef::ExternLibrary class to support dll load
This class provides a wrapper to handle loading dynamic libraries at runtime and querying symbols from libraries. The cross-platform code relies on Boost DLL library available since 1.61. Unfortunately, since Ubuntu 16.04 has only 1.58 Boost version, dynamic loading is implemented using native Linux/POSIX API. Issue #74
- Loading branch information
Showing
3 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
/* | ||
* Copyright (C) 2017 Olzhas Rakhimov | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
/// @file extern.cc | ||
/// Implementation of foreign function calls with MEF expressions. | ||
|
||
#include "extern.h" | ||
|
||
#include <boost/filesystem.hpp> | ||
#include <boost/predef.h> | ||
#include <boost/version.hpp> | ||
|
||
#if BOOST_VERSION < 106100 | ||
|
||
#if !BOOST_OS_LINUX | ||
#error "Dynamic library loading w/o Boost 1.61 is supported only on Linux." | ||
#endif | ||
|
||
#include <dlfcn.h> | ||
|
||
/// Use POSIX directly on Linux only. | ||
#define DSO_LINUX 1 | ||
|
||
#else | ||
|
||
#include <boost/dll/shared_library.hpp> | ||
#include <boost/system/system_error.hpp> | ||
|
||
#endif | ||
|
||
#include "src/error.h" | ||
|
||
namespace fs = boost::filesystem; | ||
|
||
namespace scram { | ||
namespace mef { | ||
|
||
#if DSO_LINUX | ||
/// Implementation of external library load facilities. | ||
class ExternLibrary::Pimpl { | ||
public: | ||
/// Loads the library for ExternLibrary. | ||
Pimpl(std::string lib_path, const fs::path& reference_dir, bool system, | ||
bool decorate) : lib_handle_(nullptr) { | ||
if (decorate) { | ||
lib_path += ".so"; | ||
auto pos = lib_path.find('/'); | ||
lib_path.insert(pos == std::string::npos ? 0 : (pos + 1), "lib"); | ||
} | ||
if (!system || lib_path.find('/') != std::string::npos) { | ||
fs::path abs_path = fs::absolute(lib_path, reference_dir); | ||
lib_handle_ = dlopen(abs_path.c_str(), RTLD_LAZY); | ||
} else { | ||
lib_handle_ = dlopen(lib_path.c_str(), RTLD_LAZY); | ||
} | ||
|
||
if (!lib_handle_) | ||
throw IOError(dlerror()); | ||
} | ||
|
||
/// @copydoc ExternLibrary::~ExternLibrary | ||
~Pimpl() { | ||
int err = dlclose(lib_handle_); | ||
assert(!err && "Failed to close dynamic library."); | ||
} | ||
|
||
/// Retrieves the symbol from the loaded library. | ||
void* get(const char* symbol) const { | ||
dlerror(); // Clear the error message. | ||
void* fptr = dlsym(lib_handle_, symbol); | ||
const char* err = dlerror(); | ||
if (!fptr && err) | ||
throw UndefinedElement(err); | ||
|
||
return fptr; | ||
} | ||
|
||
private: | ||
void* lib_handle_; ///< Handle to the library for reference. | ||
}; | ||
#else | ||
/// Implementation of external library load facilities. | ||
class ExternLibrary::Pimpl { | ||
public: | ||
/// Loads the library for ExternLibrary. | ||
Pimpl(std::string lib_path, const fs::path& reference_dir, bool system, | ||
bool decorate) { | ||
boost::dll::load_mode::type load_type = boost::dll::load_mode::default_mode; | ||
if (decorate) | ||
load_type |= boost::dll::load_mode::append_decorations; | ||
if (system) | ||
load_type |= boost::dll::load_mode::search_system_folders; | ||
|
||
fs::path ref_path = lib_path; | ||
if (!system || ref_path.has_parent_path()) | ||
ref_path = fs::absolute(ref_path, reference_dir); | ||
|
||
try { | ||
lib_handle_.load(ref_path, load_type); | ||
} catch (const boost::system::system_error& err) { | ||
throw IOError(err.what()); | ||
} | ||
} | ||
|
||
/// Retrieves the symbol from the loaded library. | ||
void* get(const char* symbol) const { | ||
try { | ||
return reinterpret_cast<void*>(lib_handle_.get<void()>(symbol)); | ||
} catch (const boost::system::system_error& err) { | ||
throw UndefinedElement(err.what()); | ||
} | ||
} | ||
|
||
private: | ||
boost::dll::shared_library lib_handle_; ///< Shared Library abstraction. | ||
}; | ||
#endif | ||
|
||
ExternLibrary::ExternLibrary(std::string name, std::string lib_path, | ||
const fs::path& reference_dir, bool system, | ||
bool decorate) | ||
: Element(std::move(name)), | ||
pimpl_(new Pimpl(std::move(lib_path), reference_dir, system, decorate)) {} | ||
|
||
ExternLibrary::~ExternLibrary() { delete pimpl_; } | ||
|
||
void* ExternLibrary::get(const char* symbol) const { | ||
return pimpl_->get(symbol); | ||
} | ||
|
||
} // namespace mef | ||
} // namespace scram |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* Copyright (C) 2017 Olzhas Rakhimov | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
/// @file extern.h | ||
/// The MEF facilities to call external functions in expressions. | ||
|
||
#ifndef SCRAM_SRC_EXPRESSION_EXTERN_H_ | ||
#define SCRAM_SRC_EXPRESSION_EXTERN_H_ | ||
|
||
#include <string> | ||
#include <type_traits> | ||
|
||
#include <boost/filesystem/path.hpp> | ||
#include <boost/noncopyable.hpp> | ||
|
||
#include "src/element.h" | ||
#include "src/expression.h" | ||
|
||
namespace scram { | ||
namespace mef { | ||
|
||
/// The MEF construct to extend expressions with external libraries. | ||
/// This class dynamically loads and manages libraries. | ||
/// It supports only very basic interface for C function lookup with its symbol. | ||
class ExternLibrary : public Element, private boost::noncopyable { | ||
public: | ||
/// @copydoc Element::Element | ||
/// | ||
/// @param[in] lib_path The library path with its name. | ||
/// @param[in] reference_dir The reference directory for relative paths. | ||
/// @param[in] system Search for the library in system paths. | ||
/// @param[in] decorate Decorate the library name with prefix and suffix. | ||
/// | ||
/// @throws IOError The library cannot be found. | ||
/// | ||
/// @pre The library path is canonical file path. | ||
ExternLibrary(std::string name, std::string lib_path, | ||
const boost::filesystem::path& reference_dir, bool system, | ||
bool decorate); | ||
|
||
~ExternLibrary(); ///< Closes the loaded library. | ||
|
||
/// @tparam F The C free function pointer type. | ||
/// | ||
/// @param[in] symbol The function symbol in the library. | ||
/// | ||
/// @returns The function pointer resolved from the symbol. | ||
/// | ||
/// @throws UndefinedElement The symbol is not in the library. | ||
template <typename F> | ||
std::enable_if_t<std::is_pointer<F>::value && | ||
std::is_function<std::remove_pointer_t<F>>::value, | ||
F> | ||
get(const std::string& symbol) const { | ||
return static_cast<F>(get(symbol.c_str())); | ||
} | ||
|
||
private: | ||
/// Hides all the cross-platform shared library faculties. | ||
/// @todo Remove/refactor after switching to Boost 1.61 on Linux. | ||
class Pimpl; | ||
|
||
/// @param[in] symbol The function symbol in the library. | ||
/// | ||
/// @returns The function loaded from the library symbol. | ||
/// | ||
/// @throws UndefinedElement The symbol is not in the library. | ||
void* get(const char* symbol) const; | ||
|
||
Pimpl* pimpl_; ///< Provides basic implementation for function discovery. | ||
}; | ||
|
||
} // namespace mef | ||
} // namespace scram | ||
|
||
#endif // SCRAM_SRC_EXPRESSION_EXTERN_H_ |