3

I'm writing some kind of DI container in c++ and I'm curious if it's possible to create aliases from one type to another in modern c++. What I basicly want to do is to be able to call implementation constructor by it's aliased interface. Like so:

di::Register<Interface, Impl>();
di::Resolve<Interface>(); // -> Impl should be resolved

The problem is that I've not been able to find the way to alias Interface and Impl in compile time so far. I can do this using RTTI but I really don't want to use it. Is it possible at all?

s0nicYouth
  • 470
  • 3
  • 15
  • You might have fun reading through something like [\[Boost\].DI](https://github.com/boost-experimental/di). – chris Sep 25 '17 at 16:27
  • I actually made something very similar in my [DI container library](https://github.com/gracicot/kangaru/wiki/03.-Override-Services#default-service-type) using `kgr::Default` – Guillaume Racicot Sep 25 '17 at 19:35
  • @GuillaumeRacicot yes, but I don't want to use inheritance in this case, I think it makes interface quite bulky. – s0nicYouth Sep 25 '17 at 19:59
  • @s0nicYouth inheritance was my choice of implementation. But you can also do the same thing with using statements if you modify the code a bit. – Guillaume Racicot Sep 25 '17 at 20:02

2 Answers2

1

By looking at the interface of your code, if you have a global state (which I don't actively recommend) you should get away with that:

using type_id_t = void(*)();
template<typename> void type_id() {}

struct di {
    using create_function_t = void*(*)();
    static std::unordered_map<type_id_t, create_function_t> types;

    template<typename I, typename T>
    static void Register() {
        types.emplace(type_id<I>, []{
            return static_cast<void*>(
                static_cast<I*>(new T)
            );
        });
    }

    template<typename I>
    static std::unique_ptr<I> Resolve() {
        return std::unique_ptr<I>{static_cast<I*>(types[type_id<I>]())};
    }
};

Live example

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
  • Yes, you're right. I'm going to use global state here. Although my goal is to implement something very close to dagger2. Thanks for your snipet but as I mentioned I'm looking for a way to implement it in a compile time. Something like using I = T. But your solution is a good replacement for RTTI anyway. – s0nicYouth Sep 25 '17 at 20:49
  • I don't recommend using a global state. Especially for DI. This code is pretty trivial to use without any global state. Simply remove all the static keyword and voila. – Guillaume Racicot Sep 25 '17 at 21:24
  • I got it. But for me global state here is not the problem. Main goal of my question was to find the way to bind types in compile time. Unfortunatly yor solution does't do this even if I remove global state. – s0nicYouth Sep 26 '17 at 11:02
0

The problem is that I've not been able to find the way to alias Interface and Impl in compile time so far. I can do this using RTTI but I really don't want to use it. Is it possible at all?

There is a trick you can use to do this, assuming I understood your goal correctly. It won't look as nice as what you have there:

http://stackoverflow.com/questions/4790721/c-type-registration-at-compile-time-trick

This trick was attributed to Matt Calabrese, he described it in a Boostcon talk in like 2011.

To the best of my knowledge this trick is standards-conforming, but you must be very careful -- if you do start the registration in some header files and then continue it in some CPP files you may cause an ODR violation if you are careless.

I think you would end up with some macro like

REGISTER_PAIR( interface, impl );

and then you would have some type-alias, e.g.

get_registered_impl_t<interface>

which resolves to impl.

In the example they show how to make a list of types that is accumulated over time. In your case it would be a list of type-level "pairs" and you could search it by doing linear scan. You could try to use a fancy compile-time map data structure, but in most cases its pointless since linear scan will be fast enough, and also, the length of the list you can make is bounded by the maximum template instantiation depth of your compiler, which is typically like 100 or 200 or something. If you need larger lists there are some tricks you can use but I won't detail here.

Chris Beck
  • 15,614
  • 4
  • 51
  • 87