Suppose I'm in a template and I want to know if a type parameter T is an instantiation of a particular template, e.g., std::shared_ptr:
template<typename T>
void f(T&& param)
{
if (instantiation_of(T, std::shared_ptr)) ... // if T is an instantiation of
// std::shared_ptr...
...
}
More likely I'd want to do this kind of test as part of a std::enable_if test:
template<typename T>
std::enable_if<instantiation_of<T, std::shared_ptr>::type
f(T&& param)
{
...
}
// other overloads of f for when T is not an instantiation of std::shared_ptr
Is there a way to do this? Note that the solution needs to work with all possible types and templates, including those in the standard library and in other libraries I cannot modify. My use of std::shared_ptr above is just an example of what I might want to do.
If this is possible, how would I write the test myself, i.e., implement instantiation_of?
Why use enable_if when simple overloading suffices?
template<typename T>
void f(std::shared_ptr<T> param)
{
// ...
}
If you really do need such a trait, I think this should get you started (only roughly tested with VC++ 2010):
#include <type_traits>
template<typename>
struct template_arg;
template<template<typename> class T, typename U>
struct template_arg<T<U>>
{
typedef U type;
};
template<typename T>
struct is_template
{
static T* make();
template<typename U>
static std::true_type check(U*, typename template_arg<U>::type* = nullptr);
static std::false_type check(...);
static bool const value =
std::is_same<std::true_type, decltype(check(make()))>::value;
};
template<
typename T,
template<typename> class,
bool Enable = is_template<T>::value
>
struct specialization_of : std::false_type
{ };
template<typename T, template<typename> class U>
struct specialization_of<T, U, true> :
std::is_same<T, U<typename template_arg<T>::type>>
{ };
A partial spec should be able to do it.
template <template <typename...> class X, typename T>
struct instantiation_of : std::false_type {};
template <template <typename...> class X, typename... Y>
struct instantiation_of<X, X<Y...>> : std::true_type {};
http://ideone.com/4n346
I actually had to look up the template template syntax, because I've basically never had cause to use it before.
Not sure how this interacts with templates like std::vector with additional defaulted arguments.
Best way to do it when dealing with a T&& is to make sure you remove_reference before doing the check, because the underlying type T can be a reference or a value type, and template partial specialization has to be exact to work. Combined with an answer above the code to do it could be:
template <
typename T,
template <typename...> class Templated
> struct has_template_type_impl : std::false_type {};
template <
template <typename...> class T,
typename... Ts
> struct has_template_type_impl<T<Ts...>, T> : std::true_type {};
template <
typename T,
template <typename...> class Templated
> using has_template_type = has_template_type_impl<
typename std::remove_reference<T>::type,
Templated
>;
And then you just enable_if your way to victory:
template <typename T>
typename std::enable_if<has_template_type<T, std::shared_ptr>::value>::type
f(T&& param)
{
// ...
}
Related
Is there a way to test std::is_base_of<A, B> when A is a template class?
template <typename X, typename Y> class A {};
template <typename X> class B : public A<X, char> {};
I want to statically test something like, std::is_base_of<A, B<int>> meaning, B is derived from any specialization of A.
(To make it more general, let's say we don't know the way B specializes A, i.e. B<X> derives from A<X, char>)
One way to solve would be to derived A from a (non-template) class say C, and then check std::is_base_of<C, B<int>>. But is there another way to do this?
You may do the following:
template <template <typename...> class C, typename...Ts>
std::true_type is_base_of_template_impl(const C<Ts...>*);
template <template <typename...> class C>
std::false_type is_base_of_template_impl(...);
template <typename T, template <typename...> class C>
using is_base_of_template = decltype(is_base_of_template_impl<C>(std::declval<T*>()));
Live Demo
but will fail with multiple inheritance or private inheritance from A.
With Visual Studio 2017 this will fail when the base class template has more than one template parameter, and it is unable to deduce Ts...
Demo
VS Bug Report
Refactoring solves the problem for VS.
template < template <typename...> class base,typename derived>
struct is_base_of_template_impl
{
template<typename... Ts>
static constexpr std::true_type test(const base<Ts...> *);
static constexpr std::false_type test(...);
using type = decltype(test(std::declval<derived*>()));
};
template < template <typename...> class base,typename derived>
using is_base_of_template = typename is_base_of_template_impl<base,derived>::type;
Live Demo
Bit late to the party here but I wanted to give a variant of the above
template < template <typename...> class Base,typename Derived>
struct is_base_of_template
{
// A function which can only be called by something convertible to a Base<Ts...>*
// We return a std::variant here as a way of "returning" a parameter pack
template<typename... Ts> static constexpr std::variant<Ts...> is_callable( Base<Ts...>* );
// Detector, will return type of calling is_callable, or it won't compile if that can't be done
template <typename T> using is_callable_t = decltype( is_callable( std::declval<T*>() ) );
// Is it possible to call is_callable which the Derived type
static inline constexpr bool value = std::experimental::is_detected_v<is_callable_t,Derived>;
// If it is possible to call is_callable with the Derived type what would it return, if not type is a void
using type = std::experimental::detected_or_t<void,is_callable_t,Derived>;
};
template < template <typename...> class Base,typename Derived>
using is_base_of_template_t = typename is_base_of_template<Base,Derived>::type;
template < template <typename...> class Base,typename Derived>
inline constexpr bool is_base_of_template_v = is_base_of_template<Base,Derived>::value;
This uses the proposed is_detected machanism which I think makes the intent of the test a bit clearer. However I can now get the type(s) with which the base class is instantiated at the same time which I find useful. So I can write
template <typename T, typename U> struct Foo { };
struct Bar : Foo<int,std::string> { };
static_assert( is_base_of_template_v<Foo,Bar> );
// The variant type contains the types with which the Foo base is instantiated
static_assert( std::is_same_v<std::variant<int,std::string>,is_base_of_template_t<Foo,Bar>> );
The following solution works with protected inheritance.
template <template <typename...> class BaseTemplate, typename Derived, typename TCheck = void>
struct test_base_template;
template <template <typename...> class BaseTemplate, typename Derived>
using is_base_template_of = typename test_base_template<BaseTemplate, Derived>::is_base;
//Derive - is a class. Let inherit from Derive, so it can cast to its protected parents
template <template <typename...> class BaseTemplate, typename Derived>
struct test_base_template<BaseTemplate, Derived, std::enable_if_t<std::is_class_v<Derived>>> : Derived
{
template<typename...T>
static constexpr std::true_type test(BaseTemplate<T...> *);
static constexpr std::false_type test(...);
using is_base = decltype(test((test_base_template *) nullptr));
};
//Derive - is not a class, so it is always false_type
template <template <typename...> class BaseTemplate, typename Derived>
struct test_base_template<BaseTemplate, Derived, std::enable_if_t<!std::is_class_v<Derived>>>
{
using is_base = std::false_type;
};
Surprisingly, on VS2017, it works with multiple inheritance from the same template, like C<
int > and C< float > both. (have no idea how!)
Check the
Link to test code
Based on Evgeny Mamontov answer I believe the proper solution is
template <template <typename...> class BaseTemplate, typename Derived>
struct test_base_template<BaseTemplate, Derived, std::enable_if_t<std::is_class_v<Derived>>> : Derived
{
template<typename...T>
static constexpr std::true_type test(BaseTemplate<T...> *);
static constexpr std::false_type test(...);
using is_base = decltype(test((Derived *) nullptr));
};
template<typename T> struct S {};
template<typename T> struct R {};
int main() {
typedef S<double> s1;
typedef S<int> s2;
typedef R<int> s3;
static_assert(xxx<s1, s2>::value,
"No, assertion must not be raised");
static_assert(xxx<s2, s3>::value,
"Yes, assertion must be raised");
}
So, I want xxx<s1, s2>::value to return true while xxx<s2, s3>::value to return false during compile-time.
Is the existence of xxx impossible in C++?
Or, is the existence of xxx theoretically possible in C++ but possibly no one has done it yet?
Use two specialisations that use template template parameters to perform this "matching":
template<
typename T,
typename V>
struct xxx;
template<
template <class> class A,
template <class> class B,
typename X,
typename Y>
struct xxx<A<X>, B<Y>> {
static constexpr const int value = false;
};
template<
template <class> class U,
typename X,
typename Y>
struct xxx<U<X>, U<Y>> {
static constexpr const int value = true;
};
With your code on ideone
Note: For it to be a real type trait you should not set value manually, but derive from std::integral_constant (std::true_type or std::false_type). Above is just a quick mockup I did on my phone.
Something like same_base_template:
#include <type_traits>
template<class A, class B>
struct same_base_template : std::false_type{};
template<template<class...> class S, class... U, class... V>
struct same_base_template<S<U...>, S<V...>> : std::true_type{};
Edit:
And a third specialization since you are using non-type template arguments (std::ratio):
template<class T, template<T...> class S, T... U, T... V>
struct same_base_template<S<U...>, S<V...>> : std::true_type{};
Demo
This uses true_typeand false_type from type_traits so we don't need to write a constexpr bool value ourselves. I used a variadic template here because it was slightly more generic and took only a few more keystrokes to do. For your specific use case, you don't need them)
I have traits classes sprinkled about my code which follow the same basic idiom:
template<class Frame, typename = void>
struct frame_traits
{
typedef void base_frame_type;
};
template<class Frame>
struct frame_traits<Frame, typename std::void_t<
typename Frame::base_frame_type>::type>
{
typedef typename Frame::base_frame_type base_frame_type;
};
and I have a bunch of trait checkers which use them, which also follow a similar idiom:
template <typename T>
struct has_base_frame_type : std::integral_constant<bool,
!std::is_same<typename frame_traits<T>::base_frame_type, void>::value>::type {};
however, it turns out that has_base_frame_type has become useful to multiple concepts in my code, and I'd like to generalize it further so that I can pass the traits class as an additional parameter:
template <typename T, template<typename> class Traits = frame_traits>
struct has_base_frame_type : std::integral_constant<bool,
!std::is_same<typename Traits<T>::base_frame_type, void>::value>::type {};
This doesn't work though, since templates with default arguments cannot be used as template template parameters.
I know I could work around the problem if I always use a traits class in the template instantiation (and modify the trait checker to accept it), namely
has_base_frame_type<frame_traits<MyClass>>::value
but I don't want to do that, because it would be all too easy to forget and pass in a non-trait class. In fact, that's how I originally had the code written until I forgot the trait one too many times and refactored it.
Is there someway I can modify my trait class idiom to work around the template template parameter problem?
Framework:
#include <type_traits>
template <typename...>
using void_t = void;
template <typename AlwaysVoid, template <typename...> class Operation, typename... Args>
struct detect_impl : std::false_type {};
template <template <typename...> class Operation, typename... Args>
struct detect_impl<void_t<Operation<Args...>>, Operation, Args...> : std::true_type {};
template <template <typename...> class Operation, typename... Args>
using detect = detect_impl<void, Operation, Args...>;
Detectors:
template <class Frame>
using frame_traits = typename Frame::base_frame_type;
template <class Frame>
using other_frame_traits = typename Frame::other_frame_type;
Trait with a default detector:
template <typename T, template <typename...> class Traits = frame_traits>
using has_frame_type = detect<Traits, T>;
Test:
struct A
{
using base_frame_type = void;
};
struct B
{
using other_frame_type = void;
};
int main()
{
static_assert(has_frame_type<A>{}, "!"); // default
static_assert(!has_frame_type<B>{}, "!"); // default
static_assert(!has_frame_type<A, other_frame_traits>{}, "!"); // non-default
static_assert(has_frame_type<B, other_frame_traits>{}, "!"); // non-default
}
DEMO
Say I have the following code.
template <template <typename...> class C, typename T>
constexpr bool is_vector() {
return is_same<C<T>, vector<T>>::value;
}
template <typename Value, enable_if_t<is_vector<???>()>>
void my_function(Value &value) {
}
How do I invoke is_vector in order to check if Value is an instance of the std::vector class template?
The usual way is writing another trait...
template<class C>
struct is_vector : std::false_type {};
template<class T, class A>
struct is_vector<std::vector<T,A>>: std::true_type {};
I have a class something like this:
template <typename T>
struct operation {
typedef T result_type;
typedef ::std::shared_ptr<operation<T> > ptr_t;
};
I have a functor that would match this ::std::function type:
::std::function<int(double, ::std::string)>
I want to create a functor that has a signature something like this:
operation<int>::ptr_t a_func(operation<double>::ptr_t, operation< ::std::string>::ptr_t);
I want to do this in an automated fashion so I can create a similar functor for any given ::std::function type.
Lastly, I would like to put this wrinkle in. This:
::std::function<int(operation<double>::ptr_t, ::std::string)>
should result in this:
operation<int>::ptr_t a_func(operation<double>::ptr_t, operation< ::std::string>::ptr_t);
Because if a functor already accepts an operation<T>::ptr_t that means it understands what they are and is willing to deal with their asynchronous nature itself.
How would I do this? I have a naive and partially working attempt here:
template <typename argtype>
struct transform_type {
typedef typename operation<argtype>::ptr_t type;
};
template <typename ResultType, typename... ArgTypes>
::std::function<typename transform_type<ResultType>::type(typename transform_type<ArgTypes...>::type)>
make_function(::std::function<ResultType(ArgTypes...)>)
{
return nullptr;
}
It doesn't detect arguments that are already of type std::shared_ptr<operation<T> > though. And this specialization of transform_type fails to compile:
template <typename argtype>
struct transform_type<typename operation<argtype>::ptr_t>
{
typedef typename stub_op<argtype>::ptr_t type;
};
template<template<typename...> class F, typename Sig>
struct transform;
template<template<typename...> class F, typename R, typename... A>
struct transform<F, R(A...)> {
using type = typename F<R>::ptr_t(typename F<A>::ptr_t...);
};
Usage looks like:
template<typename Sig>
void foo(std::function<Sig> f)
{
using transformed_type = typename transform<operation, Sig>::type;
std::function<transformed_type> g;
}
As for the specialization to avoid transforming types that are already in the desired form:
template<typename T>
struct operation<std::shared_ptr<T>> {
using ptr_t = std::shared_ptr<T>;
using result_type = ptr_t; // Or perhaps this needs to be T, you haven't said
};
I believe I have figured it out with R. Martinho Fernandez's help:
template <typename T>
struct is_op_ptr {
private:
// Returns false_type, which has a ::value that is false.
template <class AT>
static constexpr std::false_type is_it_a_ptr(...);
// Returns true_type (if enable_if allows it to exist).
template <class AT>
static constexpr typename ::std::enable_if<
::std::is_same<
AT,
typename operation<typename AT::element_type::result_type>::ptr_t>::value,
std::true_type>::type // note the true_type return
is_it_a_ptr(int); // no definition needed
public:
// do everything unevaluated
static constexpr bool value = decltype(is_it_a_ptr<T>(0))::value;
};
template <typename T>
struct transform_type
: ::std::conditional< is_op_ptr<T>::value, T, typename operation<T>::ptr_t>
{
};
This also allows me to query whether or not a type will be transformed in the construction of the wrapper function.