Reputation: 23
I am unable to use C++ dependency injection library "boost::di" with another boost library for dynamic loading of libraries named "Boost.dll".
I broke down the problem into two parts - firstly, Testing dynamic loading, and secondly, binding the abstract class to implementation (which is loaded dynamically).
I am able to successfully load the libraries dynamically. But when I am trying to use Dependency injection binding then It is reporting the mismatch that class template is expected and not received.
I have a very basic sample code in this repo: https://bitbucket.org/kobe_la/boost-plugins-prework/src/master/
I would really use some help in figuring out the binding process for dynamically loaded library. (see exact error at bottom)
File ioperation.hpp
#include <string>
class ioperation {
public:
virtual std::string name() const = 0;
virtual float calculate(float x, float y) = 0;
virtual ~ioperation() {}
};
File sum.cpp
#include <boost/config.hpp>
#include <boost/dll/alias.hpp>
#include <boost/dll/import.hpp>
#include "ioperation.hpp"
#include <iostream>
namespace sum_namespace {
class sum: public ioperation {
public:
sum() {
std::cout << "[sum_constructor]" << std::endl;
}
std::string name() const {
return "sum";
}
float calculate(float x, float y) {
return x + y;
}
~sum() {
std::cout << "[~sum_destructor]" << std::endl;
}
// Factory method
static boost::shared_ptr<sum> create_sum() {
return boost::shared_ptr<sum>(
new sum()
);
}
};
}
BOOST_DLL_ALIAS(
sum_namespace::sum::create_sum, // <-- this function is exported with...
create_sum // <-- ...this alias name
)
File dot_product.cpp
#include <boost/config.hpp>
#include <boost/dll/alias.hpp>
#include <boost/dll/import.hpp>
#include <iostream>
#include "ioperation.hpp"
namespace dot_product_namespace {
class dot_product: public ioperation {
boost::shared_ptr<ioperation> sum_ptr;
public:
dot_product(boost::shared_ptr<ioperation> &arg) {
sum_ptr = arg;
std::cout << "[dot_product_constructor]" << std::endl;
}
std::string name() const {
return "dot_product";
}
float calculate(float a1, float a2) {
//dot product given vector with itself
//formula: a.b = a1*b1 + a2*b2
return sum_ptr->calculate(a1*a1, a2*a2);
}
// Factory method
static boost::shared_ptr<ioperation> create_dot_product(boost::shared_ptr<ioperation> sum_ptr) {
return boost::shared_ptr<ioperation>(
new dot_product(sum_ptr)
);
}
~dot_product() {
std::cout << "[~dot_product_destructor]" << std::endl;
}
};
};
BOOST_DLL_ALIAS(
dot_product_namespace::dot_product::create_dot_product, // <-- this function is exported with...
create_dot_product // <-- ...this alias name
)
File application_di.cpp
#include "boost/shared_ptr.hpp"
#include <boost/dll/import.hpp>
#include "boost/function.hpp"
#include <boost/di.hpp>
#include "ioperation.hpp"
#include <iostream>
namespace di = boost::di;
namespace dll = boost::dll;
class app {
private:
boost::shared_ptr<ioperation> ptr_;
public:
app(boost::shared_ptr<ioperation> ptr)
: ptr_(ptr)
{
std::cout<<"name: " << ptr_->name()<<std::endl;
}
};
int main(int argc, char* argv[]) {
//setting up paths to library(.so) files
boost::filesystem::path shared_library_path("."); // argv[1] contains path to directory with our plugin library
boost::filesystem::path child_library_path = shared_library_path/"dot_product";
boost::filesystem::path parent_library_path = shared_library_path/"sum";
//defining function types for lib constructors
typedef boost::shared_ptr<ioperation> (sum_create_t)();
typedef boost::shared_ptr<ioperation> (dot_product_create_t)(boost::shared_ptr<ioperation>);
boost::function<sum_create_t> sum_creator;
boost::function<dot_product_create_t> dot_product_creator;
//importing SUM lib constructor(takes no arg)
sum_creator = boost::dll::import_alias<sum_create_t>( // type of imported symbol must be explicitly specified
parent_library_path, // path to library
"create_sum", // symbol to import
dll::load_mode::append_decorations // do append extensions and prefixes
);
//importing DOT_PRODUCT lib constructor(takes 1 arg of ptr to IOPERATION)
dot_product_creator = boost::dll::import_alias<dot_product_create_t>( // type of imported symbol must be explicitly specified
child_library_path, // path to library
"create_dot_product", // symbol to import
dll::load_mode::append_decorations // do append extensions and prefixes
);
//creating a ptr to SUM object
boost::shared_ptr<ioperation> sum_ptr = sum_creator();
//creating a ptr to DOT_PRODUCT object(passing above created SUM object ptr)
boost::shared_ptr<ioperation> dot_product_ptr = dot_product_creator(sum_ptr);
auto use_sum = true;
const auto injector = di::make_injector(
di::bind<ioperation>().to([&](const auto& injector) -> boost::shared_ptr<ioperation> {
if (use_sum)
return injector.template create<boost::shared_ptr<sum_ptr>>();
else
return injector.template create<boost::shared_ptr<dot_product_ptr>>();
})
);
injector.create<app>();
}
File application_main.cpp
#include "boost/shared_ptr.hpp"
#include <boost/dll/import.hpp>
#include "boost/function.hpp"
#include <iostream>
#include "ioperation.hpp"
namespace dll = boost::dll;
int main(int argc, char* argv[]) {
//setting up paths to library(.so) files
boost::filesystem::path shared_library_path(argv[1]); // argv[1] contains path to directory with our plugin library
boost::filesystem::path child_library_path = shared_library_path/"dot_product";
boost::filesystem::path parent_library_path = shared_library_path/"sum";
//defining function types for lib constructors
typedef boost::shared_ptr<ioperation> (sum_create_t)();
typedef boost::shared_ptr<ioperation> (dot_product_create_t)(boost::shared_ptr<ioperation>);
boost::function<sum_create_t> sum_creator;
boost::function<dot_product_create_t> dot_product_creator;
//importing SUM lib constructor(takes no arg)
sum_creator = boost::dll::import_alias<sum_create_t>( // type of imported symbol must be explicitly specified
parent_library_path, // path to library
"create_sum", // symbol to import
dll::load_mode::append_decorations // do append extensions and prefixes
);
//importing DOT_PRODUCT lib constructor(takes 1 arg of ptr to IOPERATION)
dot_product_creator = boost::dll::import_alias<dot_product_create_t>( // type of imported symbol must be explicitly specified
child_library_path, // path to library
"create_dot_product", // symbol to import
dll::load_mode::append_decorations // do append extensions and prefixes
);
//creating a ptr to PARENT_PLUGIN_SUM objecti
boost::shared_ptr<ioperation> sum_ptr = sum_creator();
//creating a ptr to CHILD_PLUGIN_MULT object(passing above created PARENT_PLUGIN_SUM object ptr)
boost::shared_ptr<ioperation> dot_product_ptr = dot_product_creator(sum_ptr);
//executing calculate methods for object ptrs
std::cout << "Plugin Name: " << sum_ptr->name() << std::endl;
std::cout << "sum_ptr->calculate(1, 2)[expected=3]: " << sum_ptr->calculate(1, 2) << std::endl;
std::cout << "Plugin Name: " << dot_product_ptr->name() << std::endl;
std::cout << "dot_product_ptr->calculate(1, 2)[expected=5]: " << dot_product_ptr->calculate(1, 2) << std::endl;
}
Below is the exact error observed:
+ echo '=> Compiling libraries and application_main.cpp ...'
=> Compiling libraries and application_main.cpp ...
+ g++ -std=c++14 -fPIC -c -o libsum.o sum.cpp
+ g++ -shared -o libsum.so libsum.o
+ g++ -std=c++14 -fPIC -c -o libdot_product.o dot_product.cpp
+ g++ -shared -o libdot_product.so libdot_product.o
+ g++ -std=c++14 -lboost_filesystem -lboost_system -ldl application_main.cpp -o application_main
+ echo '=> Compilation completed. '
=> Compilation completed.
+ echo '=> Executing ./application_main .'
=> Executing ./application_main .
+ ./application_main .
[sum_constructor]
[dot_product_constructor]
Plugin Name: sum
sum_ptr->calculate(1, 2)[expected=3]: 3
Plugin Name: dot_product
dot_product_ptr->calculate(1, 2)[expected=5]: 5
[~dot_product_destructor]
[~sum_destructor]
+ echo ==================================
==================================
+ echo '=> Compiling application_di.cpp ...'
=> Compiling application_di.cpp …
+ g++ application_di.cpp -lboost_filesystem -lboost_system -ldl -o application_di
application_di.cpp: In lambda function:
application_di.cpp:62:65: error: type/value mismatch at argument 1 in template parameter list for ‘template<class T> class boost::shared_ptr’
62 | return injector.template create<boost::shared_ptr<sum_ptr>>();
| ^~~~~~~
application_di.cpp:62:65: note: expected a type, got ‘sum_ptr’
application_di.cpp:64:65: error: type/value mismatch at argument 1 in template parameter list for ‘template<class T> class boost::shared_ptr’
64 | return injector.template create<boost::shared_ptr<dot_product_ptr>>();
| ^~~~~~~~~~~~~~~
application_di.cpp:64:65: note: expected a type, got ‘dot_product_ptr’
Upvotes: 2
Views: 1423
Reputation: 393084
Shared pointer is the problem. The bind<>
template argument cannot be a pointer or reference type
There's some information about why pointers are disallowed in
bind<>
: https://github.com/boost-ext/di/issues/317
I figured out a working way to bind the dependency:
const auto injector =
di::make_injector(di::bind<ioperation>().to(
[=, &use_sum](const auto& injector) -> op_ptr
{
return use_sum
? sum_creator()
: dot_product_creator(sum_creator());
}) //
);
Note that capturing the factory functions by value keeps the DLL around as long as the injector exists. This is safer than capturing by reference. Capturing the
use_sum
by reference highlights that the injector is dynamic. If that's not required, then I'd replace the whole injector with:const auto injector = di::make_injector(di::bind<ioperation>().to( use_sum ? sum_creator() : dot_product_creator(sum_creator())) // );
See also Github: https://github.com/sehe/boost-plugins-prework
File ioperation.hpp
#pragma once
#include <boost/shared_ptr.hpp>
#include <string>
struct ioperation {
virtual std::string name() const = 0;
virtual float calculate(float x, float y) = 0;
virtual ~ioperation() = default;
};
using op_ptr = boost::shared_ptr<ioperation>;
File sum.cpp
#include <boost/config.hpp>
#include <boost/dll/alias.hpp>
#include <boost/dll/import.hpp>
#include "ioperation.hpp"
#include <iostream>
namespace sum_namespace {
class sum : public ioperation {
public:
sum() { std::cout << "[sum_constructor]" << std::endl; }
std::string name() const { return "sum"; }
float calculate(float x, float y) { return x + y; }
~sum() { std::cout << "[~sum_destructor]" << std::endl; }
// Factory method
static op_ptr create_sum() { return op_ptr(new sum()); }
};
} // namespace sum_namespace
BOOST_DLL_ALIAS(
sum_namespace::sum::create_sum, // <-- this function is exported with...
create_sum // <-- ...this alias name
)
File sum.hpp
#pragma once
#include "ioperation.hpp"
#include "ioperation.hpp"
namespace sum_namespace {
struct sum : ioperation {
sum();
~sum() override;
std::string name() const override;
float calculate(float, float) override;
static op_ptr create_sum();
};
} // namespace sum_namespace
File dot_product.cpp
#include "dot_product.h"
#include <boost/config.hpp>
#include <boost/dll/alias.hpp>
#include <boost/dll/import.hpp>
#include <iostream>
namespace dot_product_namespace {
dot_product::dot_product(op_ptr& arg) {
sum_ptr = arg;
std::cout << "[dot_product_constructor]" << std::endl;
}
std::string dot_product::name() const { return "dot_product"; }
float dot_product::calculate(float a1, float a2) {
// dot product given vector with itself
// formula: a.b = a1*b1 + a2*b2
return sum_ptr->calculate(a1 * a1, a2 * a2);
}
// Factory method
/*static*/ op_ptr dot_product::create_dot_product(op_ptr sum_ptr) {
return op_ptr(new dot_product(sum_ptr));
}
dot_product::~dot_product() {
std::cout << "[~dot_product_destructor]" << std::endl;
}
}; // namespace dot_product_namespace
BOOST_DLL_ALIAS(dot_product_namespace::dot_product::
create_dot_product, // <-- this function is exported with...
create_dot_product // <-- ...this alias name
)
File dot_product.h
#pragma once
#include "ioperation.hpp"
namespace dot_product_namespace {
struct dot_product : ioperation {
dot_product(op_ptr&);
~dot_product() override;
std::string name() const override;
float calculate(float, float) override;
static op_ptr create_dot_product(op_ptr sum_ptr);
private:
op_ptr sum_ptr;
};
}; // namespace dot_product_namespace
File application_di.cpp
#include "boost/function.hpp"
#include "ioperation.hpp"
#include <boost/di.hpp>
#include <boost/dll/import.hpp>
#include <iostream>
namespace di = boost::di;
namespace dll = boost::dll;
class app {
private:
op_ptr ptr_;
public:
app(boost::shared_ptr<ioperation> ptr)
: ptr_(ptr)
{
std::cout << "name: " << ptr_->name() << std::endl;
}
};
using boost::filesystem::path;
int main(int argc, char** argv) {
// setting up paths to library(.so) files
path lib_path(argc > 1 ? argv[1] : "."),
child_library_path = lib_path / "dot_product",
parent_library_path = lib_path / "sum";
// defining function types for lib constructors
using sum_create_t = op_ptr();
using dot_product_create_t = op_ptr(op_ptr);
// importing SUM lib factory
auto sum_creator = boost::dll::import_alias<sum_create_t>(
parent_library_path,
"create_sum",
dll::load_mode::append_decorations);
// importing DOT_PRODUCT lib factory
auto dot_product_creator =
boost::dll::import_alias<dot_product_create_t>(
child_library_path,
"create_dot_product",
dll::load_mode::append_decorations);
auto use_sum = true;
const auto injector =
di::make_injector(di::bind<ioperation>().to(
[=, &use_sum](const auto& injector) -> op_ptr
{
return use_sum
? sum_creator()
: dot_product_creator(sum_creator());
}) //
);
use_sum = argc > 2;
injector.create<app>();
}
File application_main.cpp
#include "boost/shared_ptr.hpp"
#include <boost/dll/import.hpp>
#include "boost/function.hpp"
#include <iostream>
#include "ioperation.hpp"
namespace dll = boost::dll;
using boost::filesystem::path;
int main(int argc, char** argv)
{
// setting up paths to library(.so) files
path lib_path(argc > 1 ? argv[1] : "."),
child_library_path = lib_path / "dot_product",
parent_library_path = lib_path / "sum";
// defining function types for lib constructors
using sum_create_t = op_ptr();
using dot_product_create_t = op_ptr(op_ptr);
// importing SUM lib factory
auto sum_creator = dll::import_alias<sum_create_t>(
parent_library_path,
"create_sum",
dll::load_mode::append_decorations);
// importing DOT_PRODUCT lib factory
auto dot_product_creator =
dll::import_alias<dot_product_create_t>(
child_library_path,
"create_dot_product",
dll::load_mode::append_decorations);
auto sum_ptr = sum_creator();
auto dot_product_ptr = dot_product_creator(sum_ptr);
//executing calculate methods for object ptrs
for (op_ptr op : {sum_creator(), dot_product_creator(sum_creator()) }) {
std::cout << "\nPlugin Name: " << op->name() << "\n";
std::cout << "calculate(1, 2): " << op->calculate(1, 2) << "\n";
}
}
File compile_n_run.sh
#!/bin/bash
set -e -x -u
./cleanup.sh
echo "=> Compiling now..."
CPPFLAGS="-std=c++14 -fPIC -I /home/sehe/custom/boost-di/include/"
LDFLAGS=""
# Required to compile on sehe's machine:
#CPPFLAGS="$CPPFLAGS -I /home/sehe/custom/boost-di/include/"
#CPPFLAGS="$CPPFLAGS -isystem /home/sehe/custom/superboost/ ";
#LDFLAGS="$LDFLAGS -L /home/sehe/custom/superboost/stage/lib"
g++ $CPPFLAGS sum.cpp -shared -o libsum.so $LDFLAGS
g++ $CPPFLAGS dot_product.cpp -shared -o libdot_product.so $LDFLAGS
# add application libraries
LDFLAGS="$LDFLAGS -lboost_filesystem -lboost_system -ldl"
g++ $CPPFLAGS application_main.cpp -o application_main $LDFLAGS
g++ $CPPFLAGS application_di.cpp -o application_di $LDFLAGS
./application_main .
./application_di . use_sum
./application_di . # uses dot_product
Using compile_n_run.sh
:
+ ./cleanup.sh
removed 'application_main'
removed 'libdot_product.so'
removed 'libsum.so'
=> cleaned up!
+ echo '=> Compiling now...'
=> Compiling now...
+ CPPFLAGS='-std=c++14 -fPIC -I /home/sehe/custom/boost-di/include/'
+ LDFLAGS=
+ g++ -std=c++14 -fPIC -I /home/sehe/custom/boost-di/include/ sum.cpp -shared -o libsum.so
+ g++ -std=c++14 -fPIC -I /home/sehe/custom/boost-di/include/ dot_product.cpp -shared -o libdot_product.so
+ LDFLAGS=' -lboost_filesystem -lboost_system -ldl'
+ g++ -std=c++14 -fPIC -I /home/sehe/custom/boost-di/include/ application_main.cpp -o application_main -lboost_filesystem -lboost_system -ldl
+ g++ -std=c++14 -fPIC -I /home/sehe/custom/boost-di/include/ application_di.cpp -o application_di -lboost_filesystem -lboost_system -ldl
+ ./application_main .
[sum_constructor]
[dot_product_constructor]
[sum_constructor]
[sum_constructor]
[dot_product_constructor]
Plugin Name: sum
calculate(1, 2): 3
Plugin Name: dot_product
calculate(1, 2): 5
[~dot_product_destructor]
[~sum_destructor]
[~sum_destructor]
[~dot_product_destructor]
[~sum_destructor]
+ ./application_di . use_sum
[sum_constructor]
name: sum
[~sum_destructor]
+ ./application_di .
[sum_constructor]
[dot_product_constructor]
name: dot_product
[~dot_product_destructor]
[~sum_destructor]
Upvotes: 2