Dado
Dado

Reputation: 1187

[Boost]::DI creating unique shared_ptr objects from injector

Taking this sample code I'd like to have as a result that button1 and button2 are two separate ojects.

#include <iostream>
#include <memory>
#include "di.hpp"
namespace di = boost::di;

struct CommandQueue {
    void addCommand() {}
};

struct Control {
    Control( CommandQueue &cq ) : cq( cq ) {
        static int sid{};
        id = ++sid;
    }

    CommandQueue& cq;
    int id{};
};

int main() {

    auto injector = di::make_injector( di::bind<CommandQueue>().in(di::singleton) );

    auto button1 = injector.create<std::shared_ptr<Control>>();
    auto button2 = injector.create<std::shared_ptr<Control>>();

    std::cout << "button1->id = " << button1->id << std::endl;
    std::cout << "button2->id = " << button2->id << std::endl;

    return 0;
}

The current output is:

button1->id = 1

button2->id = 1

Instead of the intended:

button1->id = 1

button2->id = 2

Removing the di::singleton lifetime scope from CommandQueue singleton also doesn't fix it. I know the lifetime scope for a shared_ptr is a singleton by default but I thought that was referred to the injected dependency not the actual object created with create.

Upvotes: 2

Views: 1256

Answers (1)

sehe
sehe

Reputation: 393084

Indeed the simplest thing could be

auto button1 = injector.create<Control>();
auto button2 = injector.create<Control>();

std::cout << "button1.id = " << button1.id() << std::endl;
std::cout << "button2.id = " << button2.id() << std::endl;

Prints

button1.id = 1
button2.id = 2

If you must have shared-pointers, the next simplest thing would be

auto button1 = std::make_shared<Control>(injector.create<Control>());
auto button2 = std::make_shared<Control>(injector.create<Control>());

std::cout << "button1->id = " << button1->id() << std::endl;
std::cout << "button2->id = " << button2->id() << std::endl;

Conceptually you want a control-factory, not a control. So, you should consider creating a factory from the dependency container:

#include <boost/di.hpp>
#include <iostream>
#include <memory>
namespace di = boost::di;

struct CommandQueue {
    void addCommand() {}
};

struct Control {
    Control(CommandQueue &cq) : _cq(cq), _id(idgen()) { }

    int id() const { return _id; }

    struct Factory {
        Factory(CommandQueue& cq) : _cq(cq) {}
        CommandQueue& _cq;

        template <typename... Args>
        auto operator()(Args&&... args) const {
            return std::make_shared<Control>(_cq, std::forward<Args>(args)...);
        }
    };
  private:
    static int idgen() { static int sid{}; return ++sid; }
    CommandQueue &_cq;
    int _id{};
};

int main() {
    auto injector = di::make_injector(di::bind<CommandQueue>().in(di::singleton));

    auto factory = injector.create<Control::Factory>();
    auto button1 = factory();
    auto button2 = factory();

    std::cout << "button1->id = " << button1->id() << std::endl;
    std::cout << "button2->id = " << button2->id() << std::endl;
}

Upvotes: 3

Related Questions