How can I get access to this nested template parameter? - templates

Here is my code:
template<
template <typename TSEvent,
typename ...TSEvents> typename V,
typename... Filtered>
constexpr auto filter() {
if constexpr(sizeof...(TSEvents) == 0) {
return type_list<Filtered...>{};
}
if constexpr(is_default_constructible<TSEvent>::value) {
return filter<<TSEvents...>, Filtered...>();
}
return filter<<TSEvents...>, Filtered...>();
}
I however get this error, size...(TSEvents), TSEvents is not declared. Is there anyway for me to access TSEvents in my nested template?

Usually through another level of indirection, and usually a struct that we can specialize.
For example:
namespace detail
{
template<class...>
struct filter_t;
template<template<class, class...> class V, class TSEvent, class... TSEvents, class... Filtered>
struct filter_t<V<TSEvent,TSEvents...>, Filtered...>
{
static constexpr auto filter() {
return sizeof...(TSEvents);
}
};
} // detail
template<class... T>
constexpr auto filter()
{
return detail::filter_t<T...>::filter();
}
template<class T, class...U>
struct type_list{};
int main()
{
std::cout << filter<type_list<int, int, int>, int>();
}
Live Demo

Just to present another option, you could do this with only functions.
#include <iostream>
using namespace std;
template<typename...>
struct type_list{};
template < template <typename...> typename T,typename A,typename... B, typename... Filtered>
constexpr auto filter_impl(T<A,B...>*,type_list<Filtered...>)
{
using filtered_list = std::conditional_t<is_arithmetic<A>::value,
type_list<Filtered...,A>,
type_list<Filtered...>>;
if constexpr (sizeof...(B) == 0)
return filtered_list();
else
return filter_impl( (T<B...>*)0, filtered_list());
}
template <typename T>
constexpr auto filter()
{
return filter_impl( (T*)0,type_list<>());
}
struct not_arethmetic{};
int main() {
auto b = filter< type_list<not_arethmetic,int,bool,not_arethmetic,double> >();
static_assert(std::is_same< decltype(b) , type_list<int,bool,double>>::value);
return 0;
}
Demo
One thing, In your original example your first if expression will mean that the final TSEvent is not checked, as it returns if the varadic TSEvents... is zero size, but there will be one final element to check whether is_default_constructible.
Also, you might find this post useful regarding template template parameter names.

I however get this error, size...(TSEvents), TSEvents is not declared. Is there anyway for me to access TSEvents in my nested template?
Short answer: no.
Long answer: with
template<
template <typename TSEvent,
typename ...TSEvents> typename V,
typename... Filtered>
constexpr auto filter()
you set two template arguments for the filter() function.
The first one, related to the TSEvents variadic list, is a template-template argument that receive one or more types argument.
But your function doesn't receive a type that is based over that template-template (with a fixed TSEvent type and a fixed TSEvents); receive the template-template.
So doesn't make sense the test size...(TSEvents) because, for filter() isn't
fixed the TSEvents list.
To explain this in another way... you can call filter this way
filter<std::tuple, short, int, long>();
Ask for sizeof...(TSEvents) is asking how many types contains std::tuple where std::tuple is only the container of types but without contained types.
If you want to make some sort of actions in your filter() function, you need a type template parameter, not a template-template parameter.
It's simpler with classes (see AndyG's answer) where you can use partial specialization (with functions you can't) or with function when they receive arguments from which you can deduce types.
Suppose your filter() receive an object of type V<SomeTypes...> and an object of type std::tuple<Filtered...>, you can write something as follows (caution: code not tested)
template<
template <typename ...> typename V,
typename TSEvent, typename ... TSEvents, typename... Filtered>
constexpr auto filter (V<TSEvent, TSEvents...> const & v,
std::tuple<Filtered...> const & t) {
/* some code where you can use also TSEvent and TSEvents... */
}
This way TSEvent and TSEvents... are deduced from the v argument.

Related

Converting std::type_identity object to a type

Suppose that we create two type_of functions that return std::type_identity, like:
template<auto VAR>
auto type_of() {
return std::type_identity<decltype(VAR)>{};
}
template<typename T>
auto type_of() {
return std::type_identity<T>{};
}
The way to get an actual type from std::type_identity seems a bit cumbersome:
// this works
// both i1 and i2 are ints
decltype(type_of<int>())::type i1;
decltype(type_of<int{}>())::type i2;
Is there a way to waive the need for decltype in above expressions, or to put it inside a reusable expression, to achieve something nicer like:
// can this work?
type_of<int>()::type i1;
type_of<int{}>()::type i2;
Or even better:
// can this work??
type_of_t<int> i1;
type_of_t<int{}> i2;
Note: specialization for type and non-type template parameter, which could have been a direction, doesn't work (cannot compile):
template<auto>
struct type_of;
template<typename T>
struct type_of<T> { // <== compilation error: type/value mismatch
using type = T;
};
template<auto VAR>
struct type_of<VAR> {
using type = decltype(VAR);
};
You can create a type alias. However you can't "overload" it. So my solution is to create two:
template <auto Var>
using type_of_var_t = decltype(type_of<Var>())::type;
template <class T>
using type_of_t = decltype(type_of<T>())::type;
auto test()
{
type_of_var_t<11> i1 = 24;
type_of_t<int> i2 = 17;
}
In C++, a template parameter must be a value, a type, or another template (which itself must fit within the declared template header). It must be exactly one of these.
If you want to do this:
the idea is to get something that is unaware on the caller side whether the template parameter is a type or a variable
The basic requirement for being able to do this is to write a template with a parameter that could be a value or a type.
That's not a thing C++ allows.
Template function overloading allows you to get away with something like that. But that only works because it isn't one template. It's two templates that are overloaded. Which one gets selected depends on the template arguments provided.
Template classes can't be overloaded. And template specialization cannot change the nature of the original template (like what its template parameters are). It can only allow you to reinterpret the template parameters of the original template parameters in order to provide an alternative implementation.
If you want this, you're going to have to wait until either C++ gets the ability to have a template parameter that could be anything or until C++ gets the ability to convert types into values and back (ie: reflection).
Getting the type from an std::type_identity object can be encapsulated into the following expresion:
template<auto x>
using type = typename decltype(x)::type;
This would allow to replace the cumbersome expressions:
decltype(type_of<int>())::type i1;
decltype(type_of<int{}>())::type i2;
With a more simple expression:
type<type_of<int>()> i1;
type<type_of<int{}>()> i2;
It still requires to go through two steps (type_of then type) as the first one shall be able to get a type or a variable, which is applicable only with function template overloading, then the function cannot return a type, so it returns an object that needs a template expression to extract the inner type.
Depending on what you want to do with the type, the code can become even simpler.
If all you want is to create an object of that type, you can forward the creation of the object into the function:
template<auto VAR, typename... Args>
auto create_type_of(Args&&... args) {
return decltype(VAR){std::forward<Args>(args)...};
}
template<typename T, typename... Args>
auto create_type_of(Args&&... args) {
return T{std::forward<Args>(args)...};
}
auto i1 = create_type_of<int>(7);
auto i2 = create_type_of<int{}>(7);
The generic case of creating a type from std::type_identity can work this way:
template<auto VAR>
constexpr auto type_of() {
return std::type_identity<decltype(VAR)>{};
}
template<typename T>
constexpr auto type_of() {
return std::type_identity<T>{};
}
template<typename T, typename... Args>
auto create(std::type_identity<T>, Args&&... args) {
return T{std::forward<Args>(args)...};
}
auto i1 = create(type_of<int>(), 7);
auto i2 = create(type_of<int{}>(), 7);
Note that the entire thing works only with variables that can be used as non-type template parameters. For a more generic approach that works without templates (but with a macro...), and thus can work for variables that cannot be template parameters, see this mind blowing neat answer!
By design, template arguments in C++ templates are either templates, types or values.
Within the template, you know which they are. All expressions within the template that are dependent on the template arguments are disambiguated using typename or template keywords (or context) so their category is known before arguments are substituted.
Now there are metaprogramming libraries that work with value-substitutes for all 3.
template<class T> struct type_tag_t {using type=T;};
template<class T> constexpr type_tag_t<T> tag={};
template<template<class...>class Z> struct template_z_t {
template<class...Ts>
using result = Z<Ts...>;
template<class...Ts>
constexpr result<Ts...> operator()( type_tag_t<Ts>... ) const { return {}; }
};
template<template<class...>class Z>
constexpr template_z_t<Z> template_z = {};
here templates are mapped to constexpr function objects. The template_z template lets you map type-templates over to this domain.
template<auto x>
using type = typename decltype(x)::type;
template<auto x>
constexpr std::integral_constant<std::decay_t<decltype(x)>, x> k = {};
So,
constexpr auto vector_z = template_z<std::vector>;
constexpr auto vector_tag = vector_z( tag<int>, tag<std::allocator<int>> );
then you can go back to the type:
type<vector_tag> v{1,2,3,4};
this probably isn't what you want.
You might be willing to check the proposal for Universal Template Parameters
The implication example quite match your use-case of specializing for both TTP and NTTP :
template <template auto>
struct X;
template <typename T>
struct X<T> {
// T is a type
using type = T;
};
template <auto val>
struct X<val> : std::integral_constant<decltype(val), val> {
// val is an NTTP
};

Is it possible to have a "generic" template parameter in C++, that can be either a non-type template parameter or a type?

In C++, it's possible to use a type as a template parameter, e.g:
template <typename T>
void MyFn();
It's also possible to use a non-type as a template parameter in some cases, e.g.:
template <int64_t T>
void MyFn2();
My question is whether it's possible to have a "generic" template parameter that can be both? Like:
template <TypenameOrint64_t T>
void MyFn3();
such that both MyFn3<42> and MyFn3<double> would be acceptable.
An example of how I might use this:
template <typename ValType, ValType Head, ValType ...Tail>
struct ListS{
template <typename OutType, template <ValType ArgType> class Fn>
using MapHead = ListS<OutType, Fn<Head>::val, Tail...>;
};
template<int64_t N>
struct SquareS{
static constexpr const int64_t val = N * N;
};
using Sqrd = ListS<int64_t, 3, 4>::MapHead<int64_t, SquareS>;
static_assert(std::is_same<Sqrd, ListS<int64_t, 9, 4>>::value, "Values don't match");
The above is a very rough sketch of a compile-time list of values along with a single compile-time "function" on it. Would it be possible to make something like that also support lists of types, not just lists of non-type template param compatible values, without just duplicating all the code?
Is it possible to have a “generic” template parameter in C++, that can be either a non-type template parameter or a type?
Short answer: no.
Long answer.
No. The best I can imagine to mix types and values is wrap values in types, using std::integral_constant, by example.
So, your desired code, could be written (C++17) almost as follows
#include <utility>
template <typename ...>
struct ListS;
template <typename ValType, ValType Head, ValType ...Tail>
struct ListS<std::integral_constant<ValType, Head>,
std::integral_constant<ValType, Tail>...>
{
template <template <auto> class Fn, typename OutType = ValType>
using MapHead = ListS<std::integral_constant<OutType, Fn<Head>::value>,
std::integral_constant<OutType, Tail>...>;
};
template <auto N>
struct SquareS : public std::integral_constant<decltype(N), N*N>
{ };
int main ()
{
using T1 = ListS<std::integral_constant<long long, 3ll>,
std::integral_constant<long long, 4ll>>;
using T2 = T1::MapHead<SquareS>;
using T3 = ListS<std::integral_constant<long long, 9ll>,
std::integral_constant<long long, 4ll>>;
static_assert( std::is_same_v<T2, T3> );
}
Pre C++17 you can't use auto for the type of the template values so you should make some simple corrections.
You could use function overloading and the auto type deduction that came with C++17
to accomplish something similar.
template<typename myType>
auto myFn3(myType value){
return value;
}
template<auto value> //takes any non-type parameter
auto myFn3(){
return value;
}
int main(){
auto test1_normal = myFn3(3);
auto test1_cast = myFn3<double>(3); //able to perform a cast
auto test1_auto = myFn3<3>();
return 0;
}

Can I pattern-match a type without writing a custom trait class?

Since C++20 concepts aren't standardized yet, I'm using static_assert as a makeshift concept check, to provide helpful error messages if a type requirement isn't met. In this particular case, I have a function which requires that a type is callable before getting its result type:
template <typename F, typename... Args>
void example() {
static_assert(std::is_invocable_v<F, Args...>, "Function must be callable");
using R = std::invoke_result_t<F, Args...>;
// ...
}
In addition, I require that the callable's result must be some kind of std::optional, but I don't know what type the optional will hold, so I need to get that type from it:
using R = // ...
using T = typename R::value_type; // std::optional defines a value_type
However, this will fail if type R doesn't have a value_type, e.g. if it's not a std::optional as expected. I'd like to have a static_assert to check for that first, with another nice error message if the assertion fails.
I could check for an exact type with something like std::is_same_v, but in this case I don't know the exact type. I want to check that R is some instance of std::optional, without specifying which instance it must be.
One way to do that is with a helper trait:
template <typename T>
struct is_optional { static constexpr bool value = false; };
template <typename T>
struct is_optional<std::optional<T>> { static constexpr bool value = true; };
template <typename T>
constexpr bool is_optional_v = is_optional<T>::value;
…and then I can write:
static_assert(is_optional_v<R>, "Function's result must be an optional");
That works, but it seems a little awkward to pollute my namespace with a helper trait just for a one-off check like this. I don't expect to need is_optional anywhere else, though I can imagine possibly ending up with other one-off traits like is_variant or is_pair too.
So I'm wondering: is there a more concise way to do this? Can I do the pattern matching on instances of std::optional without having to define the is_optional trait and its partial specialization?
Following the suggestion by several respondents, I made a re-usable trait:
template <typename T, template <typename...> typename Tpl>
struct is_template_instance : std::false_type { };
template <template <typename...> typename Tpl, typename... Args>
struct is_template_instance<Tpl<Args...>, Tpl> : std::true_type { };
template <typename T, template <typename...> typename Tpl>
constexpr bool is_template_instance_v = is_template_instance<T, Tpl>::value;
…so that I can write:
static_assert(is_template_instance_v<R, std::optional>, "Function's result must be an optional");
This is just as many lines and declarations as the is_optional trait, but it's no longer a one-off; I can use the same trait for checking other kinds of templates (like variants and pairs). So now it feels like a useful addition to my project instead of a kluge.
Can I do the pattern matching on instances of std::optional without having to define the is_optional trait and its partial specialization?
Maybe using implicit deduction guides for std::optional?
I mean... something as
using S = decltype(std::optional{std::declval<R>()});
static_assert( std::is_same_v<R, S>, "R isn't a std::optional" );
Explanation.
When R is std::optional<T> for some T type, std::optional{r} (for an r value of type R) should call the copy constructor and the resulting value should be of the same type R.
Otherwise, the type should be different (std::optional<R>).
The following is a full compiling example.
#include <iostream>
#include <optional>
template <typename T>
bool isOptional ()
{
using U = decltype(std::optional{std::declval<T>()});
return std::is_same_v<T, U>;
}
int main ()
{
std::cout << isOptional<int>() << std::endl; // print 0
std::cout << isOptional<std::optional<int>>() << std::endl; // print 1
}
Anyway, I support the suggestion by super: create a more generic type-traits that receive std::option as template-template argument.

SFINAE with template pack: disabling member if no template arguments

I would like to define a class, with an optional template parameter such that:
if the parameter exists and it is an enum class, defines an
operator()
the class can be istantiated without the additional template
parameter (in which case operator() does not exists)
The goal is to allow access to an array element through an optional enum class.
Code:
#include <type_traits>
template <int N, typename... T>
class data {
int x[N];
template <typename T0, typename... T1>
struct enum_wrapper {
typedef T0 type;
};
public:
template <bool activate = (sizeof...(T) > 0)>
std::enable_if_t<activate, int>&
operator()(const typename enum_wrapper<T...>::type& e)
{ return x[static_cast<std::size_t>(e)]; }
};
int main()
{
data<3> a;
return 0;
}
The purpose of enum_wrapper is to single out the first parameter in the pack typename... T.
The code compiled with g++ -std=c++14 gives an error
error: wrong number of template arguments (0, should be at least 1)
on the definition of operator(). This is because enum_wrapper<T...>::type is not defined when data is istantiated with a single parameter <3>.
However, the template parameter bool activate in the definition of operator() and its return type std::enable_if_t<activate, int> is meant to prevent the instantation of operator() when the parameter pack typename... T is empty.
Why the definition of operator() is not simply discarded under SFINAE?
Also, I would like to further eliminate operator() when enum_wrapper::type is not an enum class, using std::is_enum.
Where a condition like
std::enable_if<std::is_enum_v<(enum_wrapper<T...>::type)>>
should be inserted to make SFINAE work?
The issue is that the argument type to operator() doesn't depend on the template arguments (to operator())
You can make it depend via an indirection that specifies a new parameter pack that must be the same as the class' (unfortunately we cannot default a parameter pack otherwise we would)
template <bool activate = (sizeof...(T) > 0), typename... U>
std::enable_if_t<activate && std::conjunction_v<std::is_same<T, U>...>, int>&
operator()(const typename enum_wrapper<U...>::type& e)
{ return x[static_cast<std::size_t>(e)]; }
Demo
I suppose I have some concerns as to how'd you actually plan on calling operator(), since enum_wrapper is private within the class. I think what you're really interested in is receiving a type that is the same as enum_wrapper<T...>::type. What is easiest here is to simply make a specialization for enum_wrapper for an empty T..., and then disable operator() for it:
template<class...>
struct enum_wrapper{using type = int;};
template <typename T0, typename... T1>
struct enum_wrapper<T0, T1...> {
typedef T0 type;
};
//...
template <bool activate = (sizeof...(T) > 0)>
std::enable_if_t<activate, int>&
operator()(const typename enum_wrapper<T...>::type& e)
{ return x[static_cast<std::size_t>(e)]; }
And then we could call it like so:
data<3> a; // no operator()
data<3, int> b; // operator() viable
b.x[0] = 1;
b.x[1] = 3;
b.x[2] = 5;
std::cout << b(1) << std::endl; // print '3'
Better Demo
(I made member x public for testing purposes)
In the end, it may be easier to simply move the condition that your argument to operator() is of the type enum_wrapper<T...>::type into a static_assert within the function that is protected by your bool activate:
template <class U, bool activate = (sizeof...(T) > 0)>
std::enable_if_t<activate, int>&
operator()(const U& e)
{
static_assert(std::is_same_v<U, typename enum_wrapper<T...>::type>, L"argument type to operator() is incorrect");
return x[static_cast<std::size_t>(e)];
}
You can call it like before, and there's still no operator() defined for a. If you tried to call b(2.0), though, you'd trigger the static assertion.
Best Demo

Is it possible to "bundle" template parameters in C++?

Is there a way to "bundle" template parameters together to avoid repetition?
I have several classes and functions that all use the same three template parameters. It is not unusual to have a function that uses each class/function once. The resulting code gets very messy very quickly.
Is there a more concise way to write this code?
// ContextFactory is a pointer to functions that instantiate objects that are subtypes of MetricContext
template<typename VertexID, typename EdgeMembershipType, typename SetBitmap>
using ContextFactory = MetricContext <VertexID, EdgeMembershipType, SetBitmap> *(*)(const char *);
template<typename VertexID, typename EdgeMembershipType, typename SetBitmap>
static vector<ContextFactory<VertexID, EdgeMembershipType, SetBitmap>> buildCFList() {
vector<ContextFactory<VertexID, EdgeMembershipType, SetBitmap>> answer;
answer.push_back(MetricContext<VertexID, EdgeMembershipType, SetBitmap>::template make<NeoContext<VertexID, EdgeMembershipType, SetBitmap >>);
return answer;
};
Notice that almost half of this function is repetition of the string <VertexID, EdgeMembershipType, SetBitmap>>, but each use of this string applies to a different class or function, so I don't think alias will work.
(If it helps, the purpose of this function is to create an array of pointers to functions that will create objects that are subtypes of MetricContext<VertexID, EdgeMembershipType, SetBitmap>>
A rather more specific approach than #Quentin's is to make your template depend on a single parameter - which is expected to have typedefs for VertexID, EdgeMembershipType, and SetBitmap.
// ContextFactory is a pointer to functions that instantiate objects that are
// subtypes of MetricContext
template<typename Types>
using ContextFactory = MetricContext <Types> *(*)(const char *);
template<typename Types>
static vector<ContextFactory<Types>> buildCFList() {
vector<ContextFactory<Types>> answer;
answer.push_back(MetricContext<Types>::template make<NeoContext<Types>>);
return answer;
};
Note that when you want to actually use one of the typedefs, you will need to use for example: typename Types::VertexID.
(Ideally you would come up with a better name than Types for the template argument.)
Yes, this is possible. Let's define a little helper class to hold a list of types:
template <class... > struct pack { };
And a metafunction that instantiates a template with what's inside a pack:
template <template <class... > class T, class P>
struct unpack_;
template <template <class... > class T, class... P>
struct unpack_<T, pack<P...>> {
using type = T<P...>;
};
template <template <class... > class T, class P>
using unpack = typename unpack_<T, P>::type;
And we can now store and use our parameter pack:
template <class A, class B, class C>
struct Foo { };
using Params = pack<int, float, double>;
unpack<Foo, Params> f; // f is a Foo<int, float, double>
See it live on Coliru
if you're using C++11, you can make use of std::tuple to combine the variables into one.
A simpler example to understand the same can be
template <typename A, typename B, typename C>
void fn() {
typedef std::tuple<A,B,C> myTuple;
myTuple tpl;
cout<<sizeof(std::get<0>(tpl))<<endl;;
cout<<sizeof(std::get<1>(tpl))<<endl;
cout<<sizeof(std::get<2>(tpl))<<endl;
}
int main() {
fn<int,char,long>();
return 0;
}
For the problem specific to your question, you can create vectors of tuple as
template <typename A, typename B, typename C>
void fn() {
using mycomb = std::tuple<A,B,C>;
vector<mycomb> v1;
v1.push_back(make_tuple(10,'c',20.0));
}
In this way, you don't need to repeat the same. Tuple getter functions are bit awkward at first. The cout examples above demonstrates how to access tuple parameters
Hope this helps