2

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’

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
  • 1
    Please do not post off-site links to code. Provide code samples inline as part of your question. In addition, provide the exact error message you receive and ensure the code you provided at least shows everything relevant to that message. – paddy Jul 05 '22 at 22:00
  • Can you make it work without dynamic loading? I think it has nothing to do with that, maybe with the shared_ptr? – sehe Jul 06 '22 at 00:27
  • Yes, it works without dynamic loading because then there is no need for the shared_ptr and classed can be directly accessed using include statements. – Sukhdeep Singh Jul 06 '22 at 17:23

1 Answers1

2

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())) //
);

Full Demo

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
    

Output

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]

Or more live:enter image description here

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks for the answer @sehe. But the problem is that when the libraries are loaded dynamically, they are loaded using boost shared_ptr only ( as per my knowledge in Boost DLL). For reference, I am making use of the factory method in plugin the code in question: "https://www.boost.org/doc/libs/1_64_0/doc/html/boost_dll/tutorial.html#boost_dll.tutorial.factory_method_in_plugin" – Sukhdeep Singh Jul 06 '22 at 17:20
  • The shared pointers used by Boost Dll internally have nothing to do with your function signatures – sehe Jul 06 '22 at 17:44
  • In that case, can you suggest any other way to use Boost.dll for dynamically loading the libraries which do not make use of boost shared_ptr? – Sukhdeep Singh Jul 06 '22 at 18:45
  • @SukhdeepSingh I could, but it takes work because your `ioperation` virtual type hierarchy requires pointers. I just figured it out while still using pointers, though, and I've updated my answer with a working example. – sehe Jul 06 '22 at 18:49
  • (See also Github: https://github.com/sehe/boost-plugins-prework) – sehe Jul 06 '22 at 19:12