Skip to content

Commit

Permalink
Add mef::ExternLibrary class to support dll load
Browse files Browse the repository at this point in the history
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
rakhimov committed Aug 21, 2017
1 parent 265b0fe commit b944bd0
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ set(SCRAM_CORE_SRC
"${CMAKE_CURRENT_SOURCE_DIR}/expression/exponential.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/expression/random_deviate.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/expression/test_event.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/expression/extern.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/event.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/ccf_group.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/fault_tree.cc"
Expand Down
146 changes: 146 additions & 0 deletions src/expression/extern.cc
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
90 changes: 90 additions & 0 deletions src/expression/extern.h
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_

0 comments on commit b944bd0

Please sign in to comment.