boost-ext / di

C++14 Dependency Injection Library
https://boost-ext.github.io/di
1.13k stars 136 forks source link

Bug: Strong typedef parameter registration ignored by runtime_injector #522

Open JonathanHiggs opened 3 years ago

JonathanHiggs commented 3 years ago

Setup

Testing a strong typedef parameter with the following two classes

struct NamedValue {
    int value;
};

struct Example {
    Example(NamedValue const & param, int otherValue) {
        assert(42 == param.value);
        assert(87 == otherValue);
    }
};

struct Example2 {
    Example2(NamedValue param, int otherValue) {
        assert(42 == param.value);
        assert(87 == otherValue);
    }
};

Expected Behavior

When using a static injector a strong typedef parameter is captured and delivered to constructors

auto injector = di::make_injector(
    di::bind<>.to(87),
    di::bind<>().to(NamedValue{ 42 }));

injector.create<Example>();                     // Works, param.value == 42
injector.create<Example2>();                    // Works, param.value == 42

As expected, the constructor for Example receives NamedValue param.value == 42

Actual Behavior

When working with a runtime_injector I would expect that the same setup produce the same results

However, it seems that it is not possible to register a value object like this without setting it to a singleton scope, ie. the instance scope does not work. When it is resolved the instance registered is ignored, and a new instance is created with the int resolved from the other binding

Only after registering explicitly as a singleton, resolving a singleton and setting it after the runtime_injector has had the bindings installed will the correct value be passed to the Example constructor

NamedValue nv{ 42 };

di::extension::runtime_injector runtime_injector{};
runtime_injector.install(
    di::bind<>.to(87),
    // di::bind<>().to(NamedValue{ 42 }),       // Compile error
    // di::bind<>().to(nv)                      // Compile error
    // di::bind<>().to(std::move(nv))           // Compile error
    // di::bind<>().to(NamedValue{ 42 })        // Compile error
    di::bind<>().to(NamedValue{ 42 }).in(di::singleton)
    // di::bind<>().to(NamedValue{ 42 }).in(di::deduce)    // Same behavior as singleton
    // di::bind<>().to(NamedValue{ 42 }).in(di::unique)    // Same behavior as singleton
);

runtime_injector.create<Example>();             // Doesn't work, param.value == 87
runtime_injector.create<Example2>();            // Doesn't work, param.value == 87

auto & namedValue = runtime_injector.create<NamedValue &>();
namedValue.value = 42;

runtime_injector.create<Example>();             // Works, param.value == 42
runtime_injector.create<Example2>();            // Doesn't work, param.value == 87

Steps to Reproduce the Problem

Complete sample:

#include <boost/di.hpp>
#include <boost/di/extension/providers/runtime_provider.hpp>
#include <cassert>

namespace di = boost::di;

struct NamedValue {
    int value;
};

struct Example {
    Example(NamedValue const & param, int otherValue) {
        assert(42 == param.value);
        assert(87 == otherValue);
    }
};

struct Example2 {
    Example2(NamedValue param, int otherValue) {
        assert(42 == param.value);
        assert(87 == otherValue);
    }
};

int main()
{
    auto injector = di::make_injector(
        di::bind<>.to(87),
        di::bind<>().to(NamedValue{ 42 }));

    injector.create<Example>();                     // Works, param.value == 42

    NamedValue nv{ 42 };

    di::extension::runtime_injector runtime_injector{};
    runtime_injector.install(
        di::bind<>.to(87),
        // di::bind<>().to(NamedValue{ 42 }),       // Compile error
        // di::bind<>().to(nv)                      // Compile error
        // di::bind<>().to(std::move(nv))           // Compile error
        // di::bind<>().to(NamedValue{ 42 })        // Compile error
        di::bind<>().to(NamedValue{ 42 }).in(di::singleton)
    );

    runtime_injector.create<Example>();             // Doesn't work, param.value == 87

    auto & namedValue = runtime_injector.create<NamedValue &>();
    namedValue.value = 42;

    runtime_injector.create<Example>();             // Works, param.value == 42
}

Specifications