Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polymorphic service issue #118

Closed
rafjmac opened this issue Jan 3, 2024 · 8 comments
Closed

Polymorphic service issue #118

rafjmac opened this issue Jan 3, 2024 · 8 comments
Milestone

Comments

@rafjmac
Copy link

rafjmac commented Jan 3, 2024

Hi,

I am currently investigating possibilities of the kangaru and I have a problem defining a polymorphic service which shall not be a single one:

struct Camera {
    virtual void projection() = 0;
};

struct PerspectiveCamera : Camera {
    void projection() override {
        std::cout << "perspective projection" << std::endl;
    }
};

struct CameraService : kgr::abstract_service<Camera>;
struct PerspectiveCameraService : kgr::service<PerspectiveCamera>, kgr::overrides<CameraService> {};

kgr::container container;
container.emplace<PerspectiveCameraService>();    
Camera& camera = container.service<CameraService>();

Error that pops up is about deleted function emplace<PerspectiveCameraService>().
What I am trying to get is that there is some interface class but in case anyone wants to use it, it must get a unique instance.
I haven't gone through details of the library, but I hope you can answer whether it is possible but I am doing something wrong or support for such case was not planned.

@gracicot
Copy link
Owner

gracicot commented Jan 3, 2024

We don't have first class support for non single polymorphic service, but you can definitely roll up your own service kind that does just that.

You're not the first one to ask for such feature, but I haven't find a way to implement a generic solution for this just yet.

kgr::service will always return by value, and a polymorphic override requires the service to be single. The library detects such conflict and refuse your service definition, hence your error of a deleted function.

To fix this you can create a custom service definition that contains a factory method that calls the container for a new instance, and return by std::unique_ptr. The interface is a bit hard to understand in kangaru 4, but not impossible to do. We have a documentation page for custom service definition and an example.

Let me know if you need additional support.

@rafjmac
Copy link
Author

rafjmac commented Jan 5, 2024

Hi,

Thanks for the explanation.
Also, I have one additional question - does the autowire feature work with interfaces? I read issue #111, tried to compile and it worked correctly. However, I tried to add another class that uses AbstractCamera& but it was not compiling.

@rafjmac
Copy link
Author

rafjmac commented Jan 5, 2024

I investigated the issue I had and the problem was that I added a virtual destructor - so apparently no problem with inheritance, etc. but the virtual destructor made my code to not compile. Here is the example if you want to try yourself:

#include <iostream>
#include <kangaru/kangaru.hpp>

struct autowire_abstract
{
    template <typename T> struct Service : kgr::abstract_service<T>
    {
    };

    template <typename T> using mapped_service = Service<std::decay_t<T>>;
};

template <typename... Overrides> struct autowire_override
{
    template <typename T>
    struct Service : kgr::single_service<T, kgr::autowire>, kgr::overrides<kgr::mapped_service_t<Overrides>...>
    {
    };

    template <typename T> using mapped_service = Service<std::decay_t<T>>;
};

struct autowire_polymorphic
{
    template <typename T> struct Service : kgr::single_service<T, kgr::autowire>, kgr::polymorphic
    {
    };

    template <typename T> using mapped_service = Service<std::decay_t<T>>;
};

struct AbstractCamera
{
    virtual ~AbstractCamera() = default;
    virtual void projection() = 0;
    friend auto service_map(AbstractCamera const&) -> autowire_abstract;
};

struct Camera : AbstractCamera
{
    ~Camera() = default;
    void projection() override
    {
        std::cout << "default projection" << std::endl;
    }
    friend auto service_map(Camera const&) -> autowire_override<AbstractCamera>;
};

int main()
{
    auto c = kgr::container{};

    c.emplace<kgr::mapped_service_t<Camera>>();
    c.invoke([](AbstractCamera& c) { c.projection(); });
}

@gracicot
Copy link
Owner

gracicot commented Jan 7, 2024

What compiler do you use? With GCC 13 this code compiles correctly and runs. I got the output default projection

@rafjmac
Copy link
Author

rafjmac commented Jan 7, 2024

Ah okay, I haven't checked any other one. I use Visual Studio 17 2022. Then it seems like a bug in MSVC.

@rafjmac
Copy link
Author

rafjmac commented Jan 8, 2024

Checked with Clang 17.0.6 on windows and it works.

@gracicot gracicot added msvc An issue related to msvc bug and removed question labels Jan 10, 2024
@gracicot
Copy link
Owner

Let me see what I can do to help MSVC with this one

@gracicot gracicot modified the milestones: 4.4, 4.3.2 Apr 28, 2024
@gracicot
Copy link
Owner

There's a simple workaround for this problem. Simply probe the service map using const ref types:

template <typename... Overrides> struct autowire_override
{
    template <typename T>
    struct Service : kgr::single_service<T, kgr::autowire>, kgr::overrides<kgr::mapped_service_t<Overrides const&>...>
    {
    };

    template <typename T> using mapped_service = Service<std::decay_t<T>>;
};

The root cause seems similar to issue #113 which means little can be done on my side to fix this issue without major refactoring of how the service map works, and modern technique that would make it possible are simply not available in the compilers kangaru 4 is set to support.

Since there is an easy workaround I will close this issue. Let me know if despite the workaround you still experience difficulties with autowire on msvc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants