Concept to check for variadic template function - c++

With C++20 and concepts around the corner I wondered if it will be possible to write a concept to check if a Type has a function with a certain name which takes any number of arbitrary arguments.
Take the following code for example (with GCC's current concept TS syntax):
template <typename T>
concept bool Initializable = requires(T t) {
{ t.init() } ->void;
};
struct S {
void init() {}
};
static_assert(Initializable<S>);
The concept Initializable checks if a Type implements a void init() function. Now lets assume there is another Type which also has an init function but one which requires arguments, e.g. an int:
struct T {
void init(int) {}
};
Now in this case the static assertion would fail.
Is there any way to make the Initializable concept ignore the function arguments? This example might seem rather derived, but for something like a generic serializer there might be use-cases for such a concept.

There is a type trait for that, std::is_member_function_pointer. But if you want that the return type is void too, then you can do both at the same time:
template <typename>
struct mptr_returns_void : std::false_type {};
template <typename T, typename ...Args>
struct mptr_returns_void<void(T::*)(Args...)> : std::true_type {};
template <typename T>
concept Initializable = mptr_returns_void<decltype(&T::init)>::value;

Related

Checking for a templated static method via concepts

I want to write a concept that checks if the type has a static method called foo. That method will have a templated parameter (the function will be called multiple times later with different parameter types).
Because of that templated parameter, it's quite difficult to check it. For the start, I thought I only check if there is a member at all with that name.
The following code compiles with Clang, but doesn't compile with GCC, because it cannot resolve the address of the overloaded function T::foo.
template <typename T>
concept HasFoo = requires { T::foo; };
class Bar {
public:
template <typename T>
static void foo(T t);
};
static_assert(HasFoo<Bar>);
How do you correctly check for the existence of a templated static method (working in Clang and GCC)?
And ideally, can you even check more than this? Like checking if the return type is void, or if it is callable.
One way would be to include the templated type into the concept, but as I want to use the method with multiple different types.
So checking with only one type, like in the following code, is not enough.
template <typename T, typename T2>
concept HasFoo = requires { T::template foo<T2>; };
static_assert(HasFoo<Bar, int>);
How do you correctly check for the existence of a templated static method (working in Clang and GCC)? And ideally, can you even check more than this? Like checking if the return type is void, or if it is callable.
I do have some constraints on template arguments, for the sake of the simplified example in the question we can just assume it's an integer type.
To check if the class support a static template method foo() that is callable with an integer and return void, you can simply check
template <typename T>
concept HasFoo = std::is_same_v<decltype(T::foo(0)), void>;
If you also want to be sure that the foo() method is a template one, I suppose you can also check that converting &T::foo to different function pointer types you get different values, so (for example)
( (void*)(&T::template foo<int>)
!= (void*)(&T::template foo<long>))
Combining the two requirements,
template <typename T>
concept HasFoo = ( (void*)(&T::template foo<int>)
!= (void*)(&T::template foo<long>))
&& std::is_same_v<decltype(T::foo(0)), void>;
With
struct Bar1
{ template <typename T> static void foo (T) {} };
struct Bar2
{ static void foo (int) {} };
struct Bar3
{ template <typename T> static T foo; };
template <typename T>
T Bar3::foo;
struct Bar4
{ template <typename T> static int foo (T) { return 0; } };
you have
static_assert(HasFoo<Bar1>);
static_assert(not HasFoo<Bar2>); // not because foo() isn't template
static_assert(not HasFoo<Bar3>); // not because foo isn't callable
static_assert(not HasFoo<Bar4>); // not becasue foo() return int

Working around a possible MSVC bug when deducing multiple variadic args

Here is some code that compiles in GCC (on godbolt at least - can't test locally), for handling a compile-time dependency system - the conversion operator here is to make it easier to take an entity that specifies what it can read/write and reduce it down implicitly to a more restricted form when passing into functions (please pretend that the operator below does some static_assert kind of enforcement).
template<typename... Args>
struct WriteList{};
template<typename... Args>
struct ReadList{};
template<typename Reads = ReadList<>, typename Writes = WriteList<>>
class TypedEntity;
template <typename... ReadTypes, template <typename...> typename Reads, typename... WriteTypes, template <typename...> typename Writes>
class TypedEntity<Reads<ReadTypes...>, Writes<WriteTypes...>>
{
public:
template <typename... OtherReadTypes, typename... OtherWriteTypes>
operator TypedEntity<ReadList<OtherReadTypes...>, WriteList<OtherWriteTypes...>>()
{
return {};
}
};
struct ComponentA{};
struct ComponentB{};
struct ComponentC{};
void TestFunc1(TypedEntity<ReadList<ComponentA, ComponentB>, WriteList<ComponentC>> entity)
{
}
void TestFunc2(TypedEntity<ReadList<ComponentA>, WriteList<>> entity)
{
}
void TestFunc3(TypedEntity<ReadList<ComponentA>, WriteList<ComponentC>> entity)
{
}
int main()
{
TypedEntity<ReadList<ComponentA, ComponentB>, WriteList<ComponentB>> entity;
TestFunc1(entity);
TestFunc2(entity);
TestFunc3(entity);
return 0;
}
But under MSVC (latest, i.e.g 19.28, as well as some other 19.x versions I've sampled (19.14, 19.24, etc)):
error C3547: template parameter 'OtherWriteTypes' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'TypedEntity<Reads<ReadTypes...>,Writes<WriteTypes...>>::operator TypedEntity<ReadList<OtherReadTypes...>,WriteList<OtherWriteTypes...>>'
Is this valid C++ and MSVC is wrong?
Is there a workaround for this issue in MSVC?
Appreciate it.
The error message hints at MSVC's mistake (emphasis mine):
error C3547: template parameter 'OtherWriteTypes' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'TypedEntity<Reads<ReadTypes...>,Writes<WriteTypes...>>::operator TypedEntity<ReadList<OtherReadTypes...>,WriteList<OtherWriteTypes...>>'
It's true it can't be deduced from the function parameters - but for a conversion function, deduction happens from the return type, not the empty parameter list.
So a workaround is to simplify the template signature of the conversion function. Presuming the implementation needs to know the actual ReadTypes... and WriteTypes..., the operator() definition can just call a private ordinary member function which can deduce them from parameters.
template<typename T>
struct is_WriteList_s : public std::false_type {};
template<typename... Args>
struct is_WriteList_s<WriteList<Args...>> : public std::true_type {};
template<typename T>
concept is_WriteList = is_WriteList_s<T>::value;
template<typename T>
struct is_ReadList_s : public std::false_type {};
template<typename... Args>
struct is_ReadList_s<ReadList<Args...>> : public std::true_type {};
template<typename T>
concept is_ReadList = is_ReadList_s<T>::value;
template <typename... ReadTypes, template <typename...> typename Reads,
typename... WriteTypes, template <typename...> typename Writes>
class TypedEntity<Reads<ReadTypes...>, Writes<WriteTypes...>>
{
private:
template <typename... OtherReadTypes, typename... OtherWriteTypes>
TypedEntity<ReadList<OtherReadTypes...>, WriteList<OtherWriteTypes...>>
convert_impl(std::type_identity<TypedEntity<
ReadList<OtherReadTypes...>, WriteList<OtherWriteTypes...>>>) const;
public:
template <is_ReadList OtherReadList, is_WriteList OtherWriteList>
operator TypedEntity<OtherReadList, OtherWriteList>() const
{
return convert_impl(
std::type_identity<TypedEntity<OtherReadList, OtherWriteList>>{});
}
};
I used std::type_identity just as a type-wrapper which doesn't actually have any data members or logic. If not compiling with C++20 support, any dummy template struct would do, or a raw pointer with null argument.
If using a version of MSVC or a /std: switch which does not support concepts, the concepts can be converted to SFINAE tricks or simple static_asserts.

Divorce a parameter pack in a class template

I am trying to write a class template that uses a parameter-pack and implements a member function for each type contained in the parameter-pack.
This is what I have so far:
template <typename...T>
class Myclass {
public:
void doSomething((Some_Operator_to_divorce?) T) {
/*
* Do Something
*/
std::cout << "I did something" << std::endl;
}
};
My goal is to have a class template that can be used in the following way:
Myclass<std::string, int, double> M;
M.doSomething("I am a String");
M.doSomething(1234);
M.doSomething(0.1234);
Where the class template mechanism will create an implementation for a doSomething(std::string x), a doSomething(int x) and a doSomething(double x) member function but not a doSomething(std::string x, int i, double f) member function.
I found a lot of examples in the web on the usability of parameter-packs, but I could not figure out if it can be used for my purpose, or if I totally misunderstood for what a parameter-pack can be used.
I thought that I need to unpack the parameter-pack but, after reading a lot of examples about unpacking parameter packs, I believe that this is not the right choice and it has a complete different meaning.
So, therefore, I am looking for a operation to "divorce" a parameter-pack.
There is no "operator" specifically that supports this, but what you're requesting can be done in a few different ways, depending on your requirements.
The only way to "extract" T types from a parameter pack of a class template with the purpose of implementing an overload-set of functions is to implement it using recursive inheritance, where each instance extracts one "T" type and implements the function, passing the rest on to the next implementation.
Something like:
// Extract first 'T', pass on 'Rest' to next type
template <typename T, typename...Rest>
class MyClassImpl : public MyClassImpl<Rest...>
{
public:
void doSomething(const T&) { ... }
using MyClassImpl<Rest...>::doSomething;
};
template <typename T>
class MyClassImpl<T> // end-case, no more 'Rest'
{
public:
void doSomething(const T&) { ... }
};
template <typename...Types>
class MyClass : public MyClassImpl<Types...>
{
public:
using MyClassImpl<Types...>::doSomething;
...
};
This will instantiate sizeof...(Types) class templates, where each one defines an overload for each T type.
This ensures that you get overload semantics -- such that passing an int can call a long overload, or will be ambiguous if there are two competing conversions.
However, if this is not necessary, then it'd be easier to enable the function with SFINAE using enable_if and a condition.
For exact comparisons, you could create an is_one_of trait that only ensures this exists if T is exactly one of the types. In C++17, this could be done with std::disjunction and std::is_same:
#include <type_traits>
// A trait to check that T is one of 'Types...'
template <typename T, typename...Types>
struct is_one_of : std::disjunction<std::is_same<T,Types>...>{};
Alternatively, you may want this to only work if it may work with convertible types -- which you might do something like:
template <typename T, typename...Types>
struct is_convertible_to_one_of : std::disjunction<std::is_convertible<T,Types>...>{};
The difference between the two is that if you passed a string literal to a MyClass<std::string>, it will work with the second option since it's convertible, but not the first option since it's exact. The deduced T type from the template will also be different, with the former being exactly one of Types..., and the latter being convertible (again, T may be const char*, but Types... may only contain std::string)
To work this together into your MyClass template, you just need to enable the condition with SFINAE using enable_if:
template <typename...Types>
class MyClass
{
public:
// only instantiates if 'T' is exactly one of 'Types...'
template <typename T, typename = std::enable_if_t<is_one_of<T, Types...>::value>>
void doSomething(const T&) { ... }
// or
// only instantiate if T is convertible to one of 'Types...'
template <typename T, typename = std::enable_if_t<is_convertible_to_one_of<T, Types...>::value>>
void doSomething(const T&) { ... }
};
Which solution works for you depends entirely on your requirements (overload semantics, exact calling convension, or conversion calling convension)
Edit: if you really wanted to get complex, you can also merge the two approaches... Make a type trait to determine what type would be called from an overload, and use this to construct a function template of a specific underlying type.
This is similar to how variant needs to be implemented, since it has a U constructor that considers all types as an overload set:
// create an overload set of all functions, and return a unique index for
// each return type
template <std::size_t I, typename...Types>
struct overload_set_impl;
template <std::size_t I, typename T0, typename...Types>
struct overload_set_impl<I,T0,Types...>
: overload_set_impl<I+1,Types...>
{
using overload_set_impl<I+1,Types...>::operator();
std::integral_constant<std::size_t,I> operator()(T0);
};
template <typename...Types>
struct overload_set : overload_set_impl<0,Types...> {};
// get the index that would be returned from invoking all overloads with a T
template <typename T, typename...Types>
struct index_of_overload : decltype(std::declval<overload_set<Types...>>()(std::declval<T>())){};
// Get the element from the above test
template <typename T, typename...Types>
struct constructible_overload
: std::tuple_element<index_of_overload<T, Types...>::value, std::tuple<Types...>>{};
template <typename T, typename...Types>
using constructible_overload_t
= typename constructible_overload<T, Types...>::type;
And then use this with the second approach of having a function template:
template <typename...Types>
class MyClass {
public:
// still accept any type that is convertible
template <typename T, typename = std::enable_if_t<is_convertible_to_one_of<T, Types...>::value>>
void doSomething(const T& v)
{
// converts to the specific overloaded type, and call it
using type = constructible_overload_t<T, Types...>;
doSomethingImpl<type>(v);
}
private:
template <typename T>
void doSomethingImpl(const T&) { ... }
This last approach does it two-phase; it uses the first SFINAE condition to ensure it can be converted, and then determines the appropriate type to treat it as and delegates it to the real (private) implementation.
This is much more complex, but can achieve the overload-like semantics without actually requiring recursive implementation in the type creating it.

signed/unsigned trait programming

I'm starting to learn about traits and templates in c++. What I'm wondering is is it possible to create templates for signed/unsigned integral types. The idea is that the normal class would (probably) be implemented for singed integer types, and the variation for unsigned integer types. I tried:
template <typename T>
class FXP<T>
{ ... };
template <typename T>
class FXP<unsigned T>
{ ... };
but this does not compile.
I even came across:
std::is_integral
std::is_signed
std::is_unsigned
So, how do I put these in action to define a class that only supports these two variants?
In this case, there's a few ways to go about it, but my favorite for cases where there's a limited number of variants (e.g., a boolean or two saying which way it should behave), partial specialization of a template is usually the best way to go:
// Original implementation with default boolean for the variation type
template <typename T, bool is_unsigned = std::is_unsigned<T>::value>
class FXP {
// default implementation here
};
Your next step is to then provide a partial specialization that takes the typename T, but only works for a specific variant of the template parameters (e.g. true or false).
template <typename T>
class FXP<T, false> {
// partial specialization when is_unsigned becomes false
};
template <typename T>
class FXP<T, true> {
// partial specialization when is_unsigned becomes true
};
In this case, if you write a default implementation, you only need to make a specialization for the case that's non-default (like the true case).
Here's an example, where the default case gets overridden by a specialized template parameter:
http://coliru.stacked-crooked.com/a/bc761b7b44b0d452
Note that this is better only for smaller cases. If you need complex tests, you're better off using std::enable_if and some more complicated template parameters (like in DyP's answer).
Good luck!
With an additional template parameter:
#include <iostream>
#include <type_traits>
template <typename T, class X = void>
struct FXP
{
// possibly disallow using this primary template:
// static_assert(not std::is_same<X, X>{},
// "Error: type neither signed nor unsigned");
void print() { std::cout << "non-specialized\n"; }
};
template <typename T>
struct FXP< T, typename std::enable_if<std::is_signed<T>{}>::type >
{ void print() { std::cout << "signed\n"; } };
template <typename T>
struct FXP< T, typename std::enable_if<std::is_unsigned<T>{}>::type >
{ void print() { std::cout << "unsigned\n"; } };
struct foo {};
int main()
{
FXP<foo>().print();
FXP<int>().print();
FXP<unsigned int>().print();
}

Why can't C++ infer the template type?

Why can't the compiler figure out these template parameters? Is there a way to make it do so?
(I'm using Visual Studio 2010.)
template<typename T, typename TFunc>
void call(TFunc func) { func(T()); }
void myfunc(void *) { }
int main() { call(myfunc); }
T appears nowhere in the parameter list so T cannot be deduced from the function arguments. All types to be deduced must appear in deduced contexts in the parameter list. For example,
template <typename TReturn, typename TParameter>
void call(TReturn (*f)(TParameter))
{
f(TParameter());
}
Template parameter deduction for function templates only works based on function arguments, nothing else. The function definition is never looked at for the purpose of determining the template parameters, so your parameter T cannot possibly be deduced.
You could remedy your situation by incorporating the type into the function signature: Since you expect the outer function to be called with a function itself, make that explicit:
template <typename T> void foo(void(*f)(T))
{
T x;
f(x);
// ...
}
Combine function overloading with functors, and it becomes impossible in the general case to determine what arguments can be passed to a callable entity.
Consider, for example
struct FunctorExample {
void operator()(int x) {...}
std::string operator()(const std::string& ) {...}
};
If there were some way to coax the compiler to pattern match on arguments, it would have to have undefined or error behavior when applied to FunctorExample.
Instead, the trend seems to be that when you want to template metaprogram with functors, you specify the functor and argument list. Examples (off the top of my head) being boost::result_of and boost::fusion.
Edit: That said, if you're willing to restrict your attention somewhat, and you can use some C++11 syntax (decltype), you can arrange to introspect a bit more:
// Support functors with a very simple operator():
template <typename T> struct argument :
public argument<decltype(&T::operator())> {};
// Pointers to member functions
template <typename C, typename R, typename A> struct argument<R(C::*)(A)>
{typedef A type;};
// Function types
template <typename R, typename A> struct argument<R(A)> {typedef A type;};
// Function pointer types.
template <typename R, typename A> struct argument<R(*)(A)> {typedef A type;};
// Now for call:
template <typename FuncType>
void call(FuncType func) {
typedef typename argument<FuncType>::type Arg;
func(Arg());
}
// example:
class FunctorInt {public: int operator()(int ) {return 0;};};
void myfunc(void *) {}
int main() {
call(myfunc);
call(FunctorInt());
}
Variadic templates could be used to expand this stuff to support more than one argument.