0

In general, how would you request vector<T> as a type in a template?

template<?vector<T>?>
void allocate() {
  vector<T> vector;
}

To be more specific (the example above is a bit dumb), I'm using a service_locator<T> from this library and all of its members are static. The constructor and the destructor of that struct are deleted, therefore there is no such thing as an instance of a service_locator<T>. I'd like to write a function that does something, given some service_locator<T>. Something like the following.

template<?service_locator<T>?> // What do I write here?
T& assertedRef() {
  assert(!service_locator<T>::empty(), "Value not initialized for that type!");
  return service_locator<T>::ref();
}

My current workaround is to ask for T instead of service_locator<T>. This is a hassle for the callers because it's on them to figure out the underlying type the service_locator is operating on (most of them are aliased to something more readable).

Spidey
  • 894
  • 1
  • 13
  • 29

3 Answers3

1

C++ (at least C++17) does not support template parameter constraints like C# does (they were proposed for C++0x as "concepts" but withdrawn). But you can have a static assert using std::is_base_of.

And as a hint to whoever consumes your library, I'd name the template parameter as TVector and TVectorItem to be clear that it expects a vector<TVectorItem> derivative.

(And I agree with the (now deleted) comment that the Service Locator pattern is an anti-pattern. There are almost always better alternatives to using the Service Locator pattern - but we'd need to know more about your application before suggesting anything).

(And if you're using Visual Studio or MSBuild and want to build a statically-evaluated or constexpr DI container builder, consider using T4 for codegen instead of C++ templates).

#include <type_traits>

template<typename TVector, typename TVectorItem>
void allocate() {

    static_assert( std::is_base_of<vector<TVectorItem>, TVector>::value, "TVector must derive from vector<TVectorItem>." );

    TVector vector;
}
Dai
  • 141,631
  • 28
  • 261
  • 374
  • If the caller needs to provide TVectorItem then the workaround in the question seems better. – Spidey Jan 04 '20 at 02:44
  • The service locator is providing repositories (resource loader), audio service, and a physics world (for now). Instances that live as long as the program itself. The service locator is currently used in header/cpp files with free functions where I'm doing construction and manual dependency injection (passing instances through the constructor myself). -- it's a personal hobby project – Spidey Jan 04 '20 at 02:57
1

You can use partial specialization for this. Since that's not supported for functions, you have to forward the call to a partially specialized class instance.

template <typename T>
struct AssertedRefFunctor {
    static_assert(sizeof(T) == -1, "Not a service locator");
};

template <typename T>
struct AssertedRefFunctor<entt::service_locator<T>> {
    T& operator()() {
        if (entt::service_locator<T>::empty()) {
            throw std::runtime_error("Value not initialized for that type!");
        }
        return entt::service_locator<T>::ref();
    }
};

template <typename T>
auto assertedRef() -> decltype(std::declval<AssertedRefFunctor<T>>()()) {
    return AssertedRefFunctor<T>{}();
}

Usage:

assertedRef<entt::service_locator<int>>();

Demo: https://godbolt.org/z/f-oUpY

parktomatomi
  • 3,851
  • 1
  • 14
  • 18
1

There are no direct way to do that.

In your case, you might do:

template <typename ServiceLocatorT>
auto assertedRef() -> decltype(ServiceLocatorT::ref())
{
  assert(!ServiceLocatorT::empty(), "Value not initialized for that type!");
  return ServiceLocatorT::ref();
}

In generic case, template classes might have aliases of there template (as done with STL std::vector<T>::value_type) that you can use.

At worst, you might create type_traits:

template <typename T> struct value_type;

template <typename T> struct value_type<std::vector<T>>
{
    using type = T;
};
// ...
Jarod42
  • 203,559
  • 14
  • 181
  • 302