Alias for nested variadic templated struct in VC++2013 - c++

For a templated nested struct I can define aliases like the following:
template<typename T>struct Struct
{
Struct(T value){}
template<typename T1> struct Nested
{
Nested(T1 value){}
};
};
template<typename T, typename T1>using NameT = struct Struct<T>::Nested<T1>;
using Name = NameT<int, double>; // Alias for a certain instance
Can someone please give me a hint how to declare an alias for the variadic one?
template<typename... T>struct Struct
{
Struct(T... value){}
template<typename T1> struct Nested
{
Nested(T1 value){}
};
};
template<typename... T, typename T1>using NameT = struct Struct<T...>::Nested<T1>; // This seems ok
using Name = NameT<int, double>; // error C976: 'Name': too few template arguments

This will work:
template<typename T1, typename... T>using NameT =
typename Struct<T...>::template Nested<T1>; // This seems ok
^^^^^^^^ ^^^^^^^^
using Name = NameT<int, double>;
Mind though that parameter pack must be at the end so the definition is slightly changed.
LIVE DEMO

Change your code to :
template<typename... T>
struct Struct
{
Struct(T... value){}
template<typename T1>
struct Nested
{
Nested(T1 value){}
};
};
template<typename T1, typename... T>
using NameT = typename Struct<T...>::template Nested<T1>;
using Name = NameT<int, double>;
int main()
{
Name f(1);
}
The variadic template has to be at the end of the template argument list.

#include <type_traits>
#include <tuple>
#include <utility>
template <typename... T>
struct Struct
{
Struct(T... value) {}
template <typename T1>
struct Nested
{
Nested(T1 value) {}
};
};
template <typename, typename>
struct split;
template <typename T, std::size_t... Is>
struct split<T, std::index_sequence<Is...>>
{
using type = typename Struct<typename std::tuple_element<Is, T>::type...>::template Nested<typename std::tuple_element<sizeof...(Is), T>::type>;
};
template <typename... T>
using NameT = typename split<std::tuple<T...>, std::make_index_sequence<sizeof...(T)-1>>::type;
int main()
{
static_assert(std::is_same<NameT<int, double, char>
, Struct<int, double>::Nested<char>>{}, "!");
}
DEMO

Related

Recursive concept/type_traits on tuple-like types

Say I was trying to implement a concept meowable that
Integral types are meowable.
Class types with member function meow are meowable. This is in the final target but the current question doesn't focus on it.
Tuple-like types with only meowable elements are meowable.
std::ranges::range with meowable elements are meowable. This is in the final target but the current question doesn't focus on it.
Then I came up with this implementation(simplified as I could):
#include <concepts>
#include <type_traits>
#include <ranges>
#include <utility>
#include <tuple>
template<class T>
concept meowable_builtin = std::integral<T>;
template<class T, std::size_t I>
concept has_tuple_element = requires (T t) {
typename std::tuple_element<I, T>::type;
{ get<I>(t) } -> std::convertible_to<std::tuple_element_t<I, T>&>;
};
template<class T>
concept tuple_like = requires {
typename std::tuple_size<T>::type;
{ std::tuple_size_v<T> } -> std::convertible_to<std::size_t>;
} &&
[]<std::size_t...I>(std::index_sequence<I...>) {
return (has_tuple_element<T, I> && ...);
} (std::make_index_sequence<std::tuple_size_v<T>>{});
template<class T> struct is_meowable: std::false_type{};
template<meowable_builtin T> struct is_meowable<T>: std::true_type{};
template<tuple_like T>
struct is_meowable<T>
: std::bool_constant<
[]<std::size_t...I>(std::index_sequence<I...>) {
return (is_meowable<std::tuple_element_t<I, T>>::value && ...);
} (std::make_index_sequence<std::tuple_size_v<T>>{})
> {};
template<class T>
concept meowable_tuple = tuple_like<T> && is_meowable<T>::value;
template<class T>
concept meowable = is_meowable<T>::value;
static_assert(meowable<int>);
//static_assert(tuple_like<std::tuple<int>>);
static_assert(is_meowable<std::tuple<int>>::value);
But some compilers don't like it (https://godbolt.org/z/5vMTEhTdq):
1. GCC-12 and above: internal compiler error.
2. GCC-11: accepted.
3. Clang-13 and above: static_assert fired.
4. MSVC-v19: accepted.
However, if I uncomment the second last line of code, all compilers are happy. (Instantiation point of concepts?)
So my questions are:
Why this behavior? (compiler bug or something like "ill-formed NDR"?)
How can I achieve my target?
Why this behavior? (compiler bug or something like "ill-formed NDR"?)
This is apparently a bug of GCC-trunk and Clang-trunk, the issue here is that GCC/Clang doesn't properly handle the template partial specialization based on the concept initialized by the lambda. Reduced
template<class>
concept C = [] { return true; } ();
template<class T>
struct S {};
template<class T>
requires C<T>
struct S<T> { constexpr static bool value = true; };
// static_assert(C<int>);
static_assert(S<int>::value);
How can I achieve my target?
Replace lambda with the template function based on the reduced result
template<class T, std::size_t...I>
constexpr bool all_has_tuple_element(std::index_sequence<I...>) {
return (has_tuple_element<T, I> && ...);
}
template<class T>
concept tuple_like = requires {
typename std::tuple_size<T>::type;
{ std::tuple_size_v<T> } -> std::convertible_to<std::size_t>;
} && all_has_tuple_element<T>(std::make_index_sequence<std::tuple_size_v<T>>{});
Demo
I wrote a GENERIC version (using SFINAE) with customizable predicate that will work with all the specialized tuple-like types (those that have specializations for std::tuple_element and std::tuple_size).
https://godbolt.org/z/Yf889GY6E
#include <cstddef>
#include <type_traits>
#include <tuple>
template <typename, typename = void>
struct is_complete : std::false_type {};
template <typename T>
struct is_complete<T, std::void_t<decltype(sizeof(T))>> : std::true_type {};
// catching all standard (and specialized) tuple-like types
template <typename T>
struct is_tuple_like : is_complete<std::tuple_size<T>> {};
template <typename T, bool = is_tuple_like<T>::value>
struct get_tuple_size
{
constexpr static size_t value = 0;
};
template <typename T>
struct get_tuple_size<T, true>
{
constexpr static size_t value = std::tuple_size_v<T>;
};
// generic solution with predicate
template <template <typename> class PredicateT,
typename T,
bool = is_tuple_like<T>::value,
typename = decltype(std::make_index_sequence<get_tuple_size<T>::value>{})>
struct tuple_conjunction : PredicateT<T> {};
template <template <typename> class PredicateT,
typename T,
size_t ... I>
struct tuple_conjunction<PredicateT, T, true, std::index_sequence<I...>> :
std::conjunction<tuple_conjunction<PredicateT, std::tuple_element_t<I, T>>...> {};
////////////////////////////
template <typename T, typename = void>
struct has_meow : std::false_type {};
template <typename T>
struct has_meow<T, std::void_t<decltype(std::declval<T>().mew())>> : std::true_type {};
template <typename T>
using meowable_pred = std::disjunction<std::is_integral<T>, has_meow<T>>;
template <typename T>
using meowable = tuple_conjunction<meowable_pred, T>;
#include <array>
struct cat
{
void mew() {}
};
struct dummy{};
int main()
{
static_assert(!is_complete<std::tuple_element<0, cat>>::value);
static_assert(is_complete<std::tuple_element<0, std::tuple<cat>>>::value);
static_assert(meowable<int>::value);
static_assert(meowable<cat>::value);
static_assert(!meowable<dummy>::value);
static_assert(meowable<std::tuple<long>>::value);
static_assert(meowable<std::tuple<int, long>>::value);
static_assert(meowable<std::tuple<cat, long>>::value);
static_assert(meowable<std::tuple<int, std::tuple<cat, long>>>::value);
static_assert(!meowable<std::tuple<int, std::tuple<cat, long>, dummy>>::value);
// std::array
static_assert(meowable<std::array<cat, 42>>::value);
static_assert(meowable<std::tuple<int, std::tuple<cat, long, std::tuple<std::array<cat, 42>>>>>::value);
return 0;
};
With some adjustments it will also work with C++11.
You can provide any predicate you want for a recursive check. The example shows a usage with meowable_pred predicate.
OLD ANSWER
Here is a simple recursive SFINAE solution for C++17 (and 11 for some adjustments):
https://godbolt.org/z/cezqhb99b
#include <type_traits>
template <typename T, typename = void>
struct has_meow : std::false_type {};
// this is a customization point to deduce tuple-like traits
// you can define some default logic here,
// but for the sake of example let it be false by default
template <typename>
struct is_tuple_like : std::false_type {};
template <typename T>
struct has_meow<T, std::void_t<decltype(std::declval<T>().mew())>> : std::true_type {};
template <typename T>
struct meowable : std::disjunction<std::is_integral<T>, has_meow<T>> {};
template <template <typename...> class TupleT, typename ... T>
struct meowable<TupleT<T...>> : std::conjunction<
is_tuple_like<TupleT<T...>>,
std::conjunction<meowable<T>...
> {};
#include <tuple>
#include <array>
// this will also catch std::pair
template <typename ... T>
struct is_tuple_like<std::tuple<T...>> : std::true_type {};
template <typename T, size_t N>
struct is_tuple_like<std::array<T, N>> : std::true_type {};
struct cat
{
void mew() {}
};
int main()
{
static_assert(meowable<int>::value);
static_assert(meowable<std::tuple<int, long>>::value);
static_assert(meowable<cat>::value);
static_assert(meowable<std::tuple<cat, long>>::value);
static_assert(meowable<std::tuple<int, std::tuple<cat, long>>>::value);
return 0;
};

Compare templates itselves and not instantiated template-types

My goal is to be able to compare templates i.e. write something like this
template<typename T>
struct template_type;
template<template <typename...> typename TTmpl, typename ...Ts>
struct template_type<TTmpl<Ts...>> { using type = TTmpl; }; // [1] this isn't valid C++
template<typename TRng, typename T>
auto find(TRng&& rng, const T& val)
{
using TTmpl = typename mcu::template_type<std::remove_const_t<std::remove_reference_t<TRng>>>::type;
if constexpr (std::is_same_v<TTmpl, std::set>) // [2] this isn't valid C++
{
return rng.find(val);
}
else
{
using namespace std;
return std::find(begin(rng), end(rng), val);
}
}
My second attemp was to use alias template and custom same function. Something like:
template<typename T>
struct template_type;
template<template <typename...> typename TTmpl, typename ...Ts>
struct template_type<TTmpl<Ts...>> { template<typename ...Us> using type = TTmpl<Us...>; };
template<template<typename...>typename T1, template<typename...> typename T2>
struct same : std::false_type {};
template<template<typename...>typename T>
struct same<T, T> : std::true_type {};
But this doesn't work either. The problem is that same<std::set, template_type<std::set<int>>::type>::value returns false, i.e. template class and it's template alias are different for some reason.
My third attemp was to write something like template_type_identity but I cannot figure out what should I use as identity:
template<template<typename...> typename T>
struct template_type_identity { using type = ???; }; // I could hardcode some constexpr ids for each template but I wanna generic solution
I am not sure if I understand what you want, also because I would use a different solution for your motivating case (sfinae on the presence of member find). Anyhow, this is how you can check if a given type is an instantiation of a template, provided the template has only type parameters:
#include <iostream>
#include <type_traits>
#include <set>
#include <vector>
template <template<typename...> typename T, typename C>
struct is_instantiation_of : std::false_type {};
template <template<typename...> typename T,typename ...P>
struct is_instantiation_of< T,T<P...>> : std::true_type {};
int main(){
std::cout << is_instantiation_of<std::set,std::set<int>>::value;
std::cout << is_instantiation_of<std::set,std::vector<int>>::value;
}
Output:
10

Is there a way to pass all type template parameters in an old class template into a new class template?

I've created a simple template class called tuple_tag which is identical to std::tuple but only acts as a tag.
// tuple_tag
template <typename...> struct tuple_tag {};
// tuple_tag_element
template <size_t I, typename T>
struct tuple_tag_element;
template <size_t I, typename Head, typename... Tail>
struct tuple_tag_element<I, tuple_tag<Head, Tail...>>
: tuple_tag_element<I - 1, tuple_tag<Tail...>> {};
template <typename Head, typename... Tail>
struct tuple_tag_element<0, tuple_tag<Head, Tail...>>
: std::type_identity<Head> {};
// tuple_tag_element_t
template <size_t I, typename T>
using tuple_tag_element_t = tuple_tag_element<I, T>::type;
// tuple_tag_size
template <typename T>
struct tuple_tag_size;
template <typename T> requires (std::is_reference_v<T> || std::is_const_v<T>)
struct tuple_tag_size<T> : tuple_tag_size<std::remove_cvref_t<T>> {};
template <typename... Ts>
struct tuple_tag_size<tuple_tag<Ts...>>
: std::integral_constant<size_t, sizeof...(Ts)> {};
// tuple_tag_size_v
template <typename T>
inline constexpr size_t tuple_tag_size_v = tuple_tag_size<T>::value;
Here:
using new_type_1 = to_tuple_type<tuple_tag<int, double>>::type;
// new_type_1 = std::tuple<int, double>;
using new_type_2 = to_tuple_tag_type<std::tuple<int, double>>::type;
// new_type_2 = tuple_tag<int, double>;
Where to_tuple_type takes a type template parameter tuple_tag<...> which will be converted into type std::tuple<...>, and to_tuple_tag_type takes a type template parameter std::tuple<...> which will be converted into type tuple_tag<...>.
What I am trying to achieve here is to pass all type template parameters from tuple_tag into std::tuple and vice-versa.
This is my prototype for to_tuple_type where it fails:
template <typename TupleTag>
struct to_tuple_type {
using type = std::tuple<...>;
};
Where type alias will be expanded into:
using type = std::tuple<tuple_tag_element_t<Index, TupleTag>...>;
...
using type = std::tuple<
tuple_tag_element_t<0, TupleTag>,
tuple_tag_element_t<1, TupleTag>,
...,
tuple_tag_element_t<N - 1, TupleTag>
>;
Where N is equal to tuple_tag_size_v<TupleTag>.
What I could only think of is to use std::index_sequence but I don't know where do I introduce the pack.
3 steps. First, make a pack
using indexes=std::make_index_sequence<tuple_tag_size<TupleTag>;
then have a helper that expands the pack. I like this one:
template<auto x>
using constant_t=std::integral_constant<decltype(x),x>;
template<auto x>
constexpr constant_t<x> constant={};
template<std::size_t...Is>
constexpr auto all_indexes( std::index_sequence<Is...> ){
return [](auto f){
return f(constant<Is>...);
};
}
now we can
template<class T>
struct tag_t{using type=T;};
template<class T>
constexpr tag_t<T> tag={};
using type=typename decltype(all_indexes(indexes{})([](auto...Is){
return tag<std::tuple<tuple_tag_element_t<Is, TupleTag>...>;
}))::type;
if that has no tpyos.
There is a simple solution that applies partial template specialization:
// to_tuple_type
template <typename Tup>
struct to_tuple_type;
template <typename... Ts>
struct to_tuple_type<tuple_tag<Ts...>> : std::type_identity<std::tuple<Ts...>> {};
// to_tuple_type_t
template <typename Tup>
using to_tuple_type_t = to_tuple_type<Tup>::type;
// to_tuple_tag_type
template <typename Tup>
struct to_tuple_tag_type;
template <typename... Ts>
struct to_tuple_tag_type<std::tuple<Ts...>> : std::type_identity<tuple_tag<Ts...>> {};
// to_tuple_tag_type_t
template <typename Tup>
using to_tuple_tag_type_t = to_tuple_tag_type<Tup>::type;

Resolving to different types based on the argument types of a c++ template function

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<>
>;

Is it possible to have lookup table of classes in C++?

I have a function:
template <class T, class Array>
void DumpArrayDebug(Array& out, const T& source)
It is supposed to be used to dump data from array classes in Maya (Autodesk app) into regular vector.
Example of such array types:
MFloatArray;
MColorArray;
MIntArray;
These classes have the same interface, yet they have no base class.
Currently I use this function in a following way:
MFloatArray someInternalMayaFloatData;
...
std::vector<float> buffer;
DumpArrayDebug(buffer, someInternalMayaFloatData);
Looking at this code makes me wonder is it possible to somehow tie 2 classes inside the template through something like lookup table?
So that result should look something like this:
template <class T>
void dumpArrayDbg(const T& source, ClassLookupTable<T>& out)
Thus far I was able to come up with the following monstrosity:
template <typename T>
struct ClassLookupTable
{
T classname;
};
template <>
struct ClassLookupTable<MIntArray>
{
std::vector<int> classname;
};
template <>
struct ClassLookupTable<MFloatArray>
{
std::vector<float> classname;
};
template <>
struct ClassLookupTable<MColorArray>
{
std::vector<MColor> classname;
};
template <class T>
void dumpArrayDbg(const T& source, decltype(ClassLookupTable<T>::classname)& out)
{
int length = source.length();
out.clear();
out.resize(length);
source.get(out.data());
}
Is there a more elegant solution?
This is a standard template metaprogramming technique: a traits type. The only things I would change are using standard template metaprogramming idioms (type is the standard name for a type trait, not classname) and avoid having the trait specify vector:
template <typename T>
struct MayaArrayBaseElementTrait; //The primary template should never be used.
template <>
struct MayaArrayBaseElementTrait<MIntArray>
{
using type = int;
};
template <>
struct MayaArrayBaseElementTrait<MFloatArray>
{
using type = float;
};
template <>
struct MayaArrayBaseElementTrait<MColorArray>
{
using type = MColor;
};
template<typename T>
using MayaArrayBaseElementTrait_t = typename MayaArrayBaseElementTrait<T>::type;
template <class T>
void dumpArrayDbg(const T& source, std::vector<MayaArrayBaseElementTrait_t<T>>& out)
{
int length = source.length();
out.clear();
out.resize(length);
source.get(out.data());
}
This way, the mapping is from the Maya array type to the base element type. This gives you the freedom to create mappings to other types besides vector. You could create a std::array or std::list or whatever you like. Also, if you ever want to change the allocator type for the vector, you are free to do so, unlike your original code.
Is there a more elegant solution?
I propose the following...
Given a simple template type list container
template <typename ...>
struct typeList
{ };
and a recursive template as follows
template <typename, typename>
struct lookForType
{ };
template <typename T, typename V, typename ... Ts>
struct lookForType<T, typeList<T, V, Ts...>>
{ using type = V; };
template <typename T, typename U, typename V, typename ... Ts>
struct lookForType<T, typeList<U, V, Ts...>>
: lookForType<T, typeList<Ts...>>
{ };
with an helper using to simplify the extraction of type
template <typename T, typename L>
using lookForType_t = typename lookForType<T, L>::type;
you can create the mapping as follows
using myList = typeList<MIntArray, std::vector<int>,
MFloatArray, std::vector<float>,
MColorArray, std::vector<Color>>;
and get the required type using lookForType or lookForType_t
using l1 = lookForType_t<MIntArray, myList>;
The following is a full compiling example
#include <vector>
#include <type_traits>
template <typename ...>
struct typeList
{ };
template <typename, typename>
struct lookForType
{ };
template <typename T, typename V, typename ... Ts>
struct lookForType<T, typeList<T, V, Ts...>>
{ using type = V; };
template <typename T, typename U, typename V, typename ... Ts>
struct lookForType<T, typeList<U, V, Ts...>>
: lookForType<T, typeList<Ts...>>
{ };
template <typename T, typename L>
using lookForType_t = typename lookForType<T, L>::type;
struct Color {};
struct MFloatArray {};
struct MColorArray {};
struct MIntArray {};
int main()
{
using myList = typeList<MIntArray, std::vector<int>,
MFloatArray, std::vector<float>,
MColorArray, std::vector<Color>>;
using l1 = lookForType_t<MIntArray, myList>;
using l2 = lookForType_t<MFloatArray, myList>;
using l3 = lookForType_t<MColorArray, myList>;
static_assert( std::is_same_v<std::vector<int>, l1>, "!" );
static_assert( std::is_same_v<std::vector<float>, l2>, "!" );
static_assert( std::is_same_v<std::vector<Color>, l3>, "!" );
}