gracicot / kangaru

🦘 A dependency injection container for C++11, C++14 and later
MIT License
478 stars 40 forks source link

Wrong single_service when it should be service #126

Open 01Pollux opened 4 months ago

01Pollux commented 4 months ago

Describe the bug Attempting to use container.service<> on a service that implements autowire_service, will result in copy of the object rather than move since its using single_service instead of service. https://github.com/gracicot/kangaru/blob/ab498eb9db0de74ffebc46f395b9a31093017412/include/kangaru/autowire.hpp#L169-L173

To Reproduce 1- Create main.cpp with the following content:

struct Noisy
{
    std::string Name;

    Noisy(const char* Name) :
        Name(Name)
    {
        printf("Noisy::Noisy(%s);\n", Name);
    }

    Noisy(const Noisy& o) :
        Name(o.Name)
    {
        printf("Noisy::Noisy(const Noisy&=%s);\n", Name.c_str());
    }

    Noisy(Noisy&& o) :
        Name(std::move(o.Name))
    {
        printf("Noisy::Noisy(Noisy&&=%s);\n", Name.c_str());
    }

    Noisy& operator=(const Noisy& o)
    {
        Name = o.Name;
        printf("Noisy& Noisy::operator=(const Noisy&=%s);\n", Name.c_str());
        return *this;
    }

    Noisy& operator=(Noisy&& o)
    {
        Name = o.Name;
        printf("Noisy& Noisy::operator=(Noisy&&=%s);\n", Name.c_str());
        return *this;
    }

    ~Noisy()
    {
        printf("Noisy::~Noisy(%s);\n", Name.c_str());
    }
};

struct System1 : Noisy
{
public:
    System1() :
        Noisy("System1")
    {
    }
};

struct System2 : Noisy
{
    System2() :
        Noisy("System2")
    {
    }
};

template<typename T, typename Map = kgr::map<>, std::size_t max_dependencies = kgr::detail::default_max_dependency>
using correct_autowire_service = kgr::service<T, kgr::mapped_autowire<Map, max_dependencies>>;

struct Subsystem1 : correct_autowire_service<System1>
{
};

struct Subsystem2 : kgr::autowire_service<System2>
{
};

auto service_map(System1 const&) -> Subsystem1;
auto service_map(System2 const&) -> Subsystem2;

int main()
{
    kgr::container container;

    auto a = container.service<Subsystem1>();
    auto b = container.service<Subsystem2>();

    // auto& a0 = container.service<Subsystem1>(); // triggers compilation error (correct)
    // auto& b1 = container.service<Subsystem2>(); // should not compile

    return 0;
}

Expected behavior The program should not make copies of the service but instead move it around the output for the previous sample:

Noisy::Noisy(System1);
Noisy::Noisy(Noisy&&=System1);
Noisy::~Noisy();
Noisy::Noisy(Noisy&&=System1);
Noisy::~Noisy();
Noisy::Noisy(System2);
Noisy::Noisy(const Noisy&=System2);
Noisy::~Noisy(System2);
Noisy::~Noisy(System1);
Noisy::~Noisy(System2);

Desktop (please complete the following information):

Additional context Another note i've noticed is that it moves the object two times instead of once.

gracicot commented 4 months ago

I confirm the issue. The workaround is to use kgr::service<System2, kgr::autowire> or your alias. This is caused by the alias autowire_service pointing to the wrong type.

gracicot commented 4 months ago

For the moving two time, this is a consequence of how kangaru 4 is designed. kangaru 5 won't have this issue.