c++boostc++17boost-extension

boost::ext:di create object with shared_ptr parameter through factory extension


This code is working fine :

// g++ --version == 13.2.1
// g++ -O0 -g3 -std=gnu++20 -Wall -Wextra -Wpedantic -Werror -Wfatal-errors

#include <boost/di/extension/injections/factory.hpp>
#include <cassert>
#include <memory>

class Interface
{
public:
    virtual ~Interface() noexcept = default;
};

class Implementation : public Interface
{
public:
    Implementation(int value) : value_(value) {}
    int value_ = 0;
};

class Example
{
public:
    Example(const boost::di::extension::ifactory<Interface, int>& f)
    {
        auto impl = f.create(87);
        assert(dynamic_cast<Implementation*>(impl.get()));
    }
};

namespace di = boost::di;

int main()
{
    auto injector = di::make_injector(di::bind<di::extension::ifactory<Interface, int>>().to(
        di::extension::factory<Implementation>()));
    auto example = injector.create<std::unique_ptr<Example>>();
    assert(example);
}

But this one does not compile :

#include <boost/di/extension/injections/factory.hpp>
#include <cassert>
#include <memory>

class Interface
{
public:
    virtual ~Interface() noexcept = default;
};

class Implementation : public Interface
{
public:
    Implementation(std::shared_ptr<int> value) : value_(move(value)) {}
    std::shared_ptr<int> value_;
};

class Example
{
public:
    Example(const boost::di::extension::ifactory<Interface, std::shared_ptr<int>>& f)
    {
        auto ptr = std::make_shared<int>(87);
        auto impl = f.create(std::shared_ptr<int>(ptr));  // can i just pass `ptr` ?
        assert(dynamic_cast<Implementation*>(impl.get()));
    }
};

namespace di = boost::di;

int main()
{
    auto injector =
        di::make_injector(di::bind<di::extension::ifactory<Interface, std::shared_ptr<int>>>().to(
            di::extension::factory<Implementation>()));
    auto example = injector.create<std::unique_ptr<Example>>();
    assert(example);
}

Output is :

In file included from /deps/di/extension/include/boost/di/extension/injections/factory.hpp:11,
                 from /test.cpp:1:
/deps/di/include/boost/di.hpp: In instantiation of ‘struct boost::ext::di::v1_3_0::aux::concept_check<boost::ext::di::v1_3_0::concepts::type_<std::shared_ptr<int> >::has_disallowed_qualifiers>’:
/deps/di/include/boost/di.hpp:1998:9:   required from ‘std::unique_ptr<I> boost::ext::di::v1_3_0::extension::factory_impl<TInjector, T, boost::ext::di::v1_3_0::extension::ifactory<I, TArgs ...> >::create(TArgs&& ...) const [with TInjector = boost::ext::di::v1_3_0::core::injector<boost::ext::di::v1_3_0::config, boost::ext::di::v1_3_0::core::pool<boost::ext::di::v1_3_0::aux::type_list<> >, boost::ext::di::v1_3_0::core::dependency<boost::ext::di::v1_3_0::scopes::instance, boost::ext::di::v1_3_0::extension::ifactory<Interface, std::shared_ptr<int> >, boost::ext::di::v1_3_0::extension::factory<Implementation>, boost::ext::di::v1_3_0::no_name, void, boost::ext::di::v1_3_0::core::none> >; T = Implementation; I = Interface; TArgs = {std::shared_ptr<int>}]’
/deps/di/extension/include/boost/di/extension/injections/factory.hpp:30:22:   required from here
/deps/di/include/boost/di.hpp:379:20: error: static assertion failed: constraint not satisfied
  379 |   static_assert(T::value, "constraint not satisfied");
      |                    ^~~~~
/deps/di/include/boost/di.hpp:379:20: note: ‘boost::ext::di::v1_3_0::aux::integral_constant<bool, false>::value’ evaluates to false
/deps/di/include/boost/di.hpp: In instantiation of ‘std::unique_ptr<I> boost::ext::di::v1_3_0::extension::factory_impl<TInjector, T, boost::ext::di::v1_3_0::extension::ifactory<I, TArgs ...> >::create(TArgs&& ...) const [with TInjector = boost::ext::di::v1_3_0::core::injector<boost::ext::di::v1_3_0::config, boost::ext::di::v1_3_0::core::pool<boost::ext::di::v1_3_0::aux::type_list<> >, boost::ext::di::v1_3_0::core::dependency<boost::ext::di::v1_3_0::scopes::instance, boost::ext::di::v1_3_0::extension::ifactory<Interface, std::shared_ptr<int> >, boost::ext::di::v1_3_0::extension::factory<Implementation>, boost::ext::di::v1_3_0::no_name, void, boost::ext::di::v1_3_0::core::none> >; T = Implementation; I = Interface; TArgs = {std::shared_ptr<int>}]’:
/deps/di/extension/include/boost/di/extension/injections/factory.hpp:30:22:   required from here
/deps/di/include/boost/di.hpp:1998:9: error: no type named ‘type’ in ‘struct boost::ext::di::v1_3_0::aux::concept_check<boost::ext::di::v1_3_0::concepts::type_<std::shared_ptr<int> >::has_disallowed_qualifiers>’
 1998 |         bind
      |         ^~~~

How to fix this code ? Thanks for your replies.

Regards,

XS


Solution

  • Best I understand from playing around with many (many) setups, including ditching the factory at all, is that you're simply not supposed to (smart) bind pointers to primitives, which is what the factory ends up doing too. You're supposed to bind pointers to interfaces.

    You can probably trick the system by wrapping your pointer in a value type, like this:

    Live On Compiler Explorer

    #include "https://raw.githubusercontent.com/boost-ext/di/cpp14/include/boost/di.hpp"
    #include <cassert>
    #include <iostream>
    #include <memory>
    namespace di = boost::di;
    
    template <typename T> struct Wrapped {
        std::shared_ptr<T> value;
        explicit Wrapped(T v = {}) : value(std::make_shared<T>(std::move(v))) {}
    
        // for debug output
        friend std::ostream& operator<<(std::ostream& os, Wrapped const& a) {
            return a.value ? os << *a.value : os << "#";
        }
    };
    
    // using Arg = int;
    using Arg = Wrapped<int>;
    
    struct Interface {
        virtual ~Interface() noexcept = default;
    };
    
    struct Implementation : Interface {
        Implementation(Arg value) : value_(std::move(value)) {
            std::cout << __PRETTY_FUNCTION__ << "(" << value_ << ")" << std::endl;
        }
        Arg value_{};
    };
    
    struct Example {
        Example(std::unique_ptr<Interface> impl) {
            assert(dynamic_cast<Implementation*>(impl.get()));
        }
    };
    
    int main() {
        static_assert(di::concepts::boundable<Arg>::value);
    
        auto injector = make_injector( //
            di::bind<int>().to(42),    //
            di::bind<Interface>()      //
                .to<Implementation>()  //
        );
    
        auto example = injector.create<std::unique_ptr<Example>>();
        assert(example);
    }
    

    Printing

    Implementation::Implementation(Arg)(42)
    

    Back With The Factory

    In case you really want the injectee to instantiate dynamic number of instances without directly sharing the entire injector by reference:

    #include <boost/di.hpp>
    #include <boost/di/extension/injections/factory.hpp>
    #include <cassert>
    #include <iostream>
    #include <memory>
    namespace di = boost::di;
    
    template <typename T> struct Wrapped {
        std::shared_ptr<T> value;
        /*explicit*/ Wrapped(T v = {}) : value(std::make_shared<T>(std::move(v))) {}
    
        // for debug output
        friend std::ostream& operator<<(std::ostream& os, Wrapped const& a) {
            return a.value ? os << *a.value : os << "#";
        }
    };
    
    // using Arg = int;
    using Arg = Wrapped<int>;
    
    struct Interface {
        virtual ~Interface() noexcept = default;
    };
    
    using TFactory = di::extension::ifactory<Interface>;
    
    struct Implementation : Interface {
        Implementation(Arg value) : value_(std::move(value)) {
            std::cout << __PRETTY_FUNCTION__ << "(" << value_ << ")" << std::endl;
        }
        Arg value_{};
    };
    
    struct Example {
        Example(TFactory const& f) {
            std::generate_n(back_inserter(impls), 10, [&f]{ return f.create(); });
        }
        std::vector<std::unique_ptr<Interface>> impls;
    };
    
    int main() {
        static_assert(di::concepts::boundable<Arg>::value);
    
        auto injector = make_injector(                        //
            di::bind<int>().to(43),                           //
            di::bind<TFactory>()                              //
                .to(di::extension::factory<Implementation>()) //
        );
    
        auto example = injector.create<std::unique_ptr<Example>>();
        assert(example);
    }
    

    Printing

    Implementation::Implementation(Arg)(43)
    Implementation::Implementation(Arg)(43)
    Implementation::Implementation(Arg)(43)
    Implementation::Implementation(Arg)(43)
    Implementation::Implementation(Arg)(43)
    Implementation::Implementation(Arg)(43)
    Implementation::Implementation(Arg)(43)
    Implementation::Implementation(Arg)(43)
    Implementation::Implementation(Arg)(43)
    Implementation::Implementation(Arg)(43)
    Implementation::Implementation(Arg)(43)
    

    And With Unbound Arg

    Of course this again works with the constructor argument as long as the Arg satisfies the boundable constraint again:

    using TFactory = di::extension::ifactory<Interface, Arg>;
    
    struct Example {
        Example(TFactory const& f) {
            generate_n(                   //
                back_inserter(impls), 10, //
                [i = 0, &f]() mutable { return f.create(42 * i++); });
        }
        std::vector<std::unique_ptr<Interface>> impls;
    };
    
    int main() {
        static_assert(di::concepts::boundable<Arg>::value);
    
        auto injector = make_injector(                        //
            di::bind<TFactory>()                              //
                .to(di::extension::factory<Implementation>()) //
        );
    
        auto example = injector.create<std::unique_ptr<Example>>();
        assert(example);
    }
    

    Printing

    Implementation::Implementation(Arg)(0)
    Implementation::Implementation(Arg)(42)
    Implementation::Implementation(Arg)(84)
    Implementation::Implementation(Arg)(126)
    Implementation::Implementation(Arg)(168)
    Implementation::Implementation(Arg)(210)
    Implementation::Implementation(Arg)(252)
    Implementation::Implementation(Arg)(294)
    Implementation::Implementation(Arg)(336)
    Implementation::Implementation(Arg)(378)