I have the following code:
#include <iostream>
#include <type_traits>
using namespace std;
template<typename T, int N>
class A {
public:
static constexpr int n = N;
};
template<typename T, typename X, int N>
class B {
public:
static constexpr int x = N;
};
template<typename T>
void test(T) {
if constexpr (std::is_same_v<T, A>) {
int a = T::n;
} else if constexpr (std::is_same_v<T, B>) {
int a = T::x;
}
cout << "a";
};
int main()
{
A<int, 2> a;
test(a);
return 0;
}
Compiling it produces the following error:
error: type/value mismatch at argument 2 in template parameter list for ‘template<class _Tp, class _Up> constexpr const bool std::is_same_v<_Tp, _Up>’
20 | if constexpr (std::is_same_v<T, A>) {
| ~~~~~^~~~~~~~~~~~~~~
note: expected a type, got ‘A’
The Problem is that I cannot use a correct type here (like A), since I don't know what the template parameters are or how many there are. Basically I want to match any type of class A with any template arguments.
You need to write your own trait for that:
template<typename>
struct is_specialization_of_A : std::false_type {};
template<typename T, int N>
struct is_specialization_of_A<A<T,N>> : std::true_type {};
template<typename T>
inline constexpr auto is_specialization_of_A_v = is_specialization_of_A<T>::value;
and analogously for B.
You could take the template as template template parameter of the trait as well, so that you only need one trait definition, but that only works as long as the template parameter categories of the templates match. This is not the case here (A has <type, non-type>, while B has <type, type, non-type>).
template<typename, template<typename, auto> class>
struct is_specialization_of : std::false_type {};
template<template<typename, auto> class Tmpl, typename T, auto N>
struct is_specialization_of<Tmpl<T, N>, Tmpl> : std::true_type {};
template<typename T, template<typename, auto> class Tmpl>
inline constexpr auto is_specialization_of_v = is_specialization_of<Tmpl, T>::value;
This can be used as is_specialization_of_v<A, T>, but not is_specialization_of_v<B, T>.
If you have many class templates like A and B, you can define a single type trait that would return a "tag":
struct tag_A {};
struct tag_B {};
template<class>
struct get_tag {
using type = void;
};
template<typename T, int N>
struct get_tag<A<T, N>> {
using type = tag_A;
};
template<typename T, typename X, int N>
struct get_tag<B<T, X, N>> {
using type = tag_B;
};
template<typename T>
void test(T) {
using tag = typename get_tag<T>::type;
if constexpr (std::is_same_v<tag, tag_A>) {
// ...
} else if constexpr (std::is_same_v<tag, tag_B>) {
// ...
}
}
Related
I have a user defined class
template<typename T, int N>
class MyClass
{
// Implementation
};
and I want to check on the instantiation of another class if its template parameter is an instance of MyClass
template<typename T, std::enable_if_t<!is_MyClass<T>, bool> = true>
class MapClass
{
// custom stuff here
};
template<typename T, std::enable_if_t<is_MyClass<T>, bool> = true>
class MapClass
{
// Some more stuff here
};
I tried to implement it like this but my instantiation fails because it requires two parameters. How do I make automatically extract both parameters
template <typename T> struct is_MyClass : std::false_type {};
template <typename T, int N> struct is_MyClass<MyClass<T, N>> : std::true_type {};
Thanks
I suggest to write a trait is_instantiation_of_myClass that uses partial specialization:
template<typename T, int N>
class MyClass {};
template <typename C>
struct is_instantiation_of_myClass : std::false_type {};
template <typename T,int N>
struct is_instantiation_of_myClass<MyClass<T,N>> : std::true_type {};
template <typename C>
constexpr bool is_instantiation_of_myClass_v = is_instantiation_of_myClass<C>::value;
Now you can do SFINAE based on is_instantiation_of_myClass<T> or just plain specialization:
template <typename T,bool = is_instantiation_of_myClass_v<T>>
struct Foo;
template <typename T>
struct Foo<T,true> {
static constexpr bool value = true;
};
template <typename T>
struct Foo<T,false> {
static constexpr bool value = false;
};
int main() {
std::cout << Foo< int >::value << "\n";
std::cout << Foo< MyClass<int,42>>::value << "\n";
}
Live Demo
I find myself wanting/needing to use a variadic composite type as a template parameter. Unfortunately, std::tuple<> is not a structural type, which makes the obvious approach a no-go:
#include <tuple>
template<typename... Ts>
struct composite {
std::tuple<Ts...> operands;
};
template<auto v>
void foo() {}
int main() {
constexpr composite tmp{std::make_tuple(1,2,3)};
foo<tmp>(); // <----- Nope!
}
Is there a reasonable way to build such a composite in a manner that works as a structural type?
Since the MCVE in isolation is trivially solvable as "just make foo() a variadic template", here's a more representative example:
On godbolt
#include <concepts>
#include <tuple>
template <typename T, template <typename...> typename U>
concept TemplatedConfig = requires(T x) {
{ U(x) } -> std::same_as<T>;
// A few more things identifying T as a valid config
};
template<auto Config>
struct proc;
// Basic
struct Basic {};
template<Basic v>
struct proc<v> {
constexpr int foo() { return 0; }
};
// Annotated
template<typename T>
struct Annotated {
T v;
int annotation;
};
template<TemplatedConfig<Annotated> auto v>
struct proc<v> {
constexpr int foo() { return 1; }
};
// ... more config / specialization pairs ...
// Composite
template<typename... Parts>
struct Composite {
std::tuple<Parts...> parts;
};
template<TemplatedConfig<Composite> auto v>
struct proc<v> {
constexpr int foo() { return 2; }
};
int main() {
constexpr Basic a = Basic{};
constexpr Annotated b{a, 12};
constexpr Composite c{std::make_tuple(a, b)};
static_assert(proc<a>{}.foo() == 0);
static_assert(proc<b>{}.foo() == 1);
static_assert(proc<c>{}.foo() == 2); <----- :(
}
Edit: If anyone is curious what a (almost) fully-realized tuple class based on the accepted answer looks like: https://gcc.godbolt.org/z/ThaGjbo67
Create your own structural tuple-like class?
Something like:
template <std::size_t I, typename T>
struct tuple_leaf
{
T data;
};
template <typename T> struct tag{ using type = T; };
template <typename Seq, typename...>
struct tuple_impl;
template <std::size_t... Is, typename... Ts>
struct tuple_impl<std::index_sequence<Is...>, Ts...> : tuple_leaf<Is, Ts>...
{
constexpr tuple_impl(Ts... args) : tuple_leaf<Is, Ts>{args}... {}
};
template <typename T, std::size_t I> constexpr const T& get(const tuple_leaf<I, T>& t) { return t.data; }
template <typename T, std::size_t I> constexpr T& get(tuple_leaf<I, T>& t) { return t.data; }
template <std::size_t I, typename T> constexpr const T& get(const tuple_leaf<I, T>& t) { return t.data; }
template <std::size_t I, typename T> constexpr T& get(tuple_leaf<I, T>& t) { return t.data; }
template <std::size_t I, typename T>
tag<T> tuple_element_tag(const tuple_leaf<I, T>&);
template <std::size_t I, typename Tuple>
using tuple_element = decltype(tuple_element_tag<I>(std::declval<Tuple>()));
template <std::size_t I, typename Tuple>
using tuple_element_t = typename tuple_element<I, Tuple>::type;
template <typename ... Ts>
using tuple = tuple_impl<std::make_index_sequence<sizeof...(Ts)>, Ts...>;
Demo
I'm doing some metaprogramming and I have ran into the following problem:
I have a class that takes one template parameter T, T can be assumed to be a function with an arbitary signature. The class a member variable V, that should have the type std::tuple<> if T takes no arguments or the first argument is not a std::tuple. If the first argument is an std::tuple, V should instead have the same type as first argument.
Example:
void f() // Should resolve to std::tuple<>
void f(int) // Should resolve to std::tuple<>
void f(std::tuple<int, float>) // Should resolve to std::tuple<int, float>
void f(std::tuple<float>, int) // Should resolve to std::tuple<float>
I have been trying something similar to this, but with no success. As it fails when indexing the first arguement on the argument free function, without selecting any of the other alternatives in spite of those being available. I'm using MSVC 2019 16.8.4
#include <functional>
#include <concepts>
namespace detail
{
template<typename... ArgTs>
struct HasArgs : public std::conditional<(sizeof... (ArgTs) > 0), std::true_type, std::false_type>::type {};
}
//!
//! Provides argument function information
//! Based on: https://stackoverflow.com/a/9065203
//!
template<typename T>
class FunctionTraits;
template<typename R, typename... Args>
class FunctionTraits<R(Args...)>
{
public:
static const size_t arg_count = sizeof...(Args);
using HasArguments = detail::HasArgs<Args...>;
using ReturnType = R;
using ArgTypes = std::tuple<Args...>;
template <size_t i>
struct arg
{
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};
namespace detail
{
template <typename T>
struct is_tuple : std::false_type {};
template <typename... Args>
struct is_tuple<std::tuple<Args...>>: std::true_type {};
}
template <typename T>
concept is_tuple = requires() { detail::is_tuple<T>::value; };
class TestMemberFunctions
{
public:
static int test_f1(std::tuple<int, float>, int)
{
return 0;
}
static int test_f2(int)
{
return 0;
}
static int test_f3()
{
return 0;
}
};
template <typename CreateT> requires (!FunctionTraits<CreateT>::HasArguments::value)
std::tuple<> TypeDeductionDummyFunction();
template <typename CreateT> requires FunctionTraits<CreateT>::HasArguments::value
auto TypeDeductionDummyFunction() -> std::conditional<is_tuple<typename FunctionTraits<CreateT>::template arg<0>::type>,
typename FunctionTraits<CreateT>::template arg<0>::type,
std::tuple<>>;
template <typename T>
class SampleClass
{
decltype(TypeDeductionDummyFunction<T>()) m_member;
};
SampleClass<decltype(TestMemberFunctions::test_f1)> c1;
SampleClass<decltype(TestMemberFunctions::test_f2)> c2;
SampleClass<decltype(TestMemberFunctions::test_f3)> c3;
Something along these lines, perhaps:
template <typename T> struct ExtractFirstTuple;
template <typename R>
struct ExtractFirstTuple<R()> {
using type = std::tuple<>;
};
template <typename R, typename... Ts, typename... Args>
struct ExtractFirstTuple<R(std::tuple<Ts...>, Args...)> {
using type = std::tuple<Ts...>;
};
template <typename R, typename First, typename... Args>
struct ExtractFirstTuple<R(First, Args...)> {
using type = std::tuple<>;
};
Demo
An attempt to build what you want from more primitive operations.
template<typename T, std::size_t N>
struct FunctionArgument {
static constexpr bool exists = false;
};
template<typename R, typename A0, typename... Args>
struct FunctionArgument<R(A0, Args...), 0>{
using type=A0;
static constexpr bool exists = true;
};
template<typename R, typename A0, typename... Args, std::size_t N>
struct FunctionArgument<R(A0, Args...), N>:
FunctionArgument<R(Args...), N-1>
{};
template<class Sig, std::size_t N>
using FuncArg_type = typename FunctionArgument<Sig, N>::type;
template<class Sig, std::size_t N>
constexpr bool FuncArg_exists = FunctionArgument<Sig, N>::exists;
template<class Sig, class Otherwise>
using FirstArgIfExists =
typename std::conditional_t<
FuncArg_exists<Sig,0>,
FunctionArgument<Sig, 0>,
std::type_identity<Otherwise>
>::type;
template<class T, class Otherwise>
struct TypeIfTuple {
using type=Otherwise;
};
template<class...Ts, class Otherwise>
struct TypeIfTuple<std::tuple<Ts...>, Otherwise> {
using type=std::tuple<Ts...>;
};
template<class T, class Otherwise>
using TypeIfTuple_t = typename TypeIfTuple<T,Otherwise>::type;
template<class Sig>
using TheTypeYouWant = TypeIfTuple_t<
FirstArgIfExists<Sig, std::tuple<>>,
std::tuple<>
>;
#include <iostream>
/**** I am confused to apply sfinae method here ******/
template <typename T>
struct hasTypeFoo {
//..
static constexpr bool value = true;
};
/////////////////////////////////////////////////
struct A {
using Foo = int;
};
struct B {
};
int main()
{
constexpr bool b1 = hasTypeFoo<A>::value;
constexpr bool b2 = hasTypeFoo<B>::value;
std::cout << b1 << b2;
}
Use std::void_t:
template<typename T, typename = void>
struct hasTypeFoo : std::false_type { };
template<typename T>
struct hasTypeFoo<T, std::void_t<typename T::Foo>> : std::true_type { };
A very good explanation of how std::void_t works, can be found in this question. It is used here to silently reject the specialization if typename T::Foo is ill-formed.
You can do it with partial specialization. e.g.
// primary template
template <typename T, typename = void>
struct hasTypeFoo {
static constexpr bool value = false;
};
// partial specialization for types containing type Foo
template <typename T>
struct hasTypeFoo<T, std::void_t<typename T::Foo>> {
static constexpr bool value = true;
};
LIVE
I have a templated struct like this:
class Context {
template<typename T>
T construct();
};
template<typename T, typename ...Args>
struct aggregate {
T construct(Context& ctx) {
return { std::move(ctx.construct<Args>())... };
}
};
The problem with this is easy to see: When a user requests uses it like this:
typedef struct {
float xyz[3];
} Vector3;
using ConstructVector = aggregate<Vector3, float[3]>;
Here an error is generated because this would call the templated function Context.construct with T = float[3] resulting in a function returning an array. What I thus want is some way to expand { std::move(ctx.construct<Args>())... } to this: {{ std::move(ctx.construct<float>()), std::move(ctx.construct<float>()), std::move(ctx.construct<float>()) }} or more general, any array of type T (with constexpr size N) should be extended to a construct repeating std::move(ctx.construct<T>()) exactly N times and should be wrapped in an additional pair of {}.
Is this a wrong approach? Is there another way to initialize an array of values in an aggregate statement?
I get the following error from g++ (4.9.1):
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/utilitysupplier.hpp: In instantiation of 'typename io::Supplier<T>::item_t io::utility::aggregate_supplier<T, args>::supply(io::Context&) const [with T = Vector3; args = float [3]; typename io::Supplier<T>::item_t = Vector3]':
../test/main.cpp:185:1: required from here
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/utilitysupplier.hpp:42:37: error: no matching function for call to 'io::Context::construct()'
return { ctx.construct<args>()... };
^
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/utilitysupplier.hpp:42:37: note: candidate is:
In file included from C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Context.hpp:141:0,
from C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Reader.hpp:13,
from C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Environment.hpp:12,
from ../test/main.cpp:14:
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Context.tpp:37:28: note: template<class T> typename io::supply_t<T>::type io::Context::construct()
typename supply_t<T>::type Context::construct() {
^
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Context.tpp:37:28: note: template argument deduction/substitution failed:
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Context.tpp: In substitution of 'template<class T> typename io::supply_t<T>::type io::Context::construct() [with T = float [3]]':
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/utilitysupplier.hpp:42:37: required from 'typename io::Supplier<T>::item_t io::utility::aggregate_supplier<T, args>::supply(io::Context&) const [with T = Vector3; args = float [3]; typename io::Supplier<T>::item_t = Vector3]'
../test/main.cpp:185:1: required from here
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Context.tpp:37:28: error: function returning an array
Generating a nested braced-init list is impossible, but generating a flatten one is possible and is suitable for aggregate.
The approach shown below uses C++14 though, with the help of std::make_index_sequence, but you can implement such a thing in C++11 as well:
template<class... Ts>
struct list;
template<class A, class B>
struct merge;
template<class... As, class... Bs>
struct merge<list<As...>, list<Bs...>>
{
using type = list<As..., Bs...>;
};
template<std::size_t N, class T>
using just = T;
template<class T, class Index>
struct repeat_impl;
template<class T, std::size_t... Ns>
struct repeat_impl<T, std::index_sequence<Ns...>>
{
using type = list<just<Ns, T>...>;
};
template<class T, int N>
using repeat = typename repeat_impl<T, std::make_index_sequence<N>>::type;
template<class T>
struct to_list
{
using type = list<T>;
};
template<class T, int N>
struct to_list<T[N]>
{
using type = repeat<T, N>;
};
template<class... Ts>
struct flatten;
template<>
struct flatten<>
{
using type = list<>;
};
template<class T, class... Ts>
struct flatten<T, Ts...>
{
using type = typename merge<typename to_list<T>::type, typename flatten<Ts...>::type>::type;
};
flatten<float[3], int[2]>::type will return you list<float, float, float, int, int, int>.
Now we can implement aggregate as below:
struct Context
{
template<typename T>
T construct();
};
template<class T, class List>
struct aggregate_impl;
template<class T, class... Args>
struct aggregate_impl<T, list<Args...>>
{
static T construct(Context& ctx)
{
return {ctx.construct<Args>()...};
}
};
template<class T, class... Args>
using aggregate = aggregate_impl<T, typename flatten<Args...>::type>;
Now you can do:
using ConstructVector = aggregate<Vector3, float[3]>;
Context ctx;
ConstructVector::construct(ctx);
LIVE DEMO