I'm trying to detect if a specific overload for my function is callable. I assumed I could do something similar to this answer, but I believe the issue is that the function signature template<typename From, typename To> convert(const From&) is well defined, but the instantiation is not.
#include <iostream>
#include <string>
template<typename From, typename To>
To convert(const From& from)
{
// I have a lot of additional template specializations for this function
return from;
}
template<typename From, typename To>
struct IsConvertible
{
template<typename = decltype(convert<From, To>(From()))>
static std::true_type test(int);
template<typename T>
static std::false_type test(...);
static bool const value = decltype(test(0))::value;
};
int main()
{
std::cout << "IsConvertible=" << IsConvertible<int, float>::value << std::endl;
// Returns 1 as expected
std::cout << "IsConvertible=" << IsConvertible<int, std::string>::value << std::endl;
// Returns 1, expected 0. The issue seems to be that decltype(convert<From, To>(From()))
// is somehow ok, although convert<int, std::string>(1) definitly isn't
}
I want to use IsConvertible for some additional metaprogramming. Is it possible to detect if the template<typename From, typename To> To convert(const From&) function is actually callable?`
With declaration of
template<typename From, typename To> To convert(const From& from);
Your traits
template<typename From, typename To>
struct IsConvertible
would always detect presence of convert function.
One way to fix it is overloads and/or SFINAE:
template <typename> struct Tag{};
int convertImpl(tag<int>, const std::string& from);
float convertImpl(tag<float>, const std::string& from);
// overloads ...
template<typename From, typename To>
auto convert(const From& from)
-> decltype(convertImpl(tag<To>{}, from))
{
return convertImpl(tag<To>{}, from);
}
I might have misunderstood your question but is not the using std::is_invocable enough in this case as showcased in the following?
#include<type_traits>
template<typename From, typename To>
To convert(const From& from)
{
// I have a lot of additional template specializations for this function
return from;
}
template<>
std::string convert(const int& from)
{
//silly specialization
return "2"+from;
}
struct Foo{
int bar;
};
int main()
{
//ok specialization is called
std::cout<<std::is_invocable<decltype(convert<int,std::string>),std::string>::value<<std::endl;
//no way I can convert int to Foo, specialization required
std::cout<<std::is_invocable<decltype(convert<int,Foo>),Foo>::value<<std::endl;
return 0;
}
I see some problems in your code.
Without a particular order...
(1) SFINAE, using decltype(), check only the presence of a declared function; doesn't check if that function is defined or if it's definition works (compile) or not.
I propose you to rewrite convert() using directly SFINAE to declare it only when is compilable
template <typename To, typename From,
decltype( To(std::declval<From>()), bool{} ) = true>
To convert (From const & f)
{ return f; }
This way convert() is declared only if you can construct a To object starting from a From object.
(2) Observe that I've also switched the order of To and From: this way you can call the convert() function explicating only the To type
convert<float>(0); // From is deduced as int from the 0 value
If you declare To (that isn't deducible) after From (that is deducible), you necessarily have to explicit both types, calling the function, also when the From type is deducible.
(3) Your IsConvertible struct doesn't works.
It's a common error using SFINAE.
When you write
template<typename = decltype(convert<From, To>(From()))>
static std::true_type test(int);
you're trying enable/disable this test() method using SFINAE over From and To that are the template parameters of the struct
Wrong.
SFINAE works over template parameters of the method itself.
If you want to use SFINAE, you have to transform From and To in template parameters of the method; by example
template <typename F = From, typename T = To,
typename = decltype(convert<F, T>(std::declval<F>()))>
static std::true_type test(int);
Now SFINAE uses F and T that are template parameters of the test() method and this is correct.
(4) Observe that I've written std::declval<F>() instead of F(). It's because you're not sure that F (From) is default constructible. With std::declval() you go around this problem.
I propose a different IsConvertible custom type traits that take in count the From/To inversion and demand to the value call of test() the From+To->F+T type conversion
template <typename To, typename From>
struct IsConvertible
{
template <typename T, typename F,
typename = decltype(convert<T>(std::declval<F>()))>
static std::true_type test(int);
template <typename...>
static std::false_type test(...);
static bool const value = decltype(test<To, From>(0))::value;
};
(5) you're expecting that
IsConvertible<int, std::string>::value
is zero; but you forgetting that std::string is constructible from int; so this value (or IsConvertible<std::string, int>, switching To and From) should be one.
The following is a corrected full working example
#include <iostream>
#include <string>
#include <vector>
template <typename To, typename From,
decltype( To(std::declval<From>()), bool{} ) = true>
To convert (From const & f)
{ return f; }
template <typename To, typename From>
struct IsConvertible
{
template <typename T, typename F,
typename = decltype(convert<T>(std::declval<F>()))>
static std::true_type test(int);
template <typename...>
static std::false_type test(...);
static bool const value = decltype(test<To, From>(0))::value;
};
int main ()
{
std::cout << "IsConvertible=" << IsConvertible<float, int>::value
<< std::endl;
std::cout << "IsConvertible=" << IsConvertible<int, std::string>::value
<< std::endl;
}
Related
I have a traits class like the following that reflects the compatibility between two types:
template <typename ObjectType, typename ArgumentType>
struct Traits
{
static const bool SpecialMethodAvailable = false;
};
The single member determines if SpecialMethod() can be called on objects of type ObjectType with argument of type ArgumentType.
A simple class that supports this is the following:
class ClassWithSpecialMethod
{
public:
template <typename T>
void SpecialMethod(T param) { std::cout << "Special Method called with " << param << std::endl; }
};
template <typename ArgumentType>
struct Traits<ClassWithSpecialMethod, ArgumentType>
{
static const bool SpecialMethodAvailable = true;
};
I want to write a worker class that uses this traits class and calls the special method if it is available. Basically something like the following:
template <typename T>
struct Worker
{
static void DoSomething(T t, GlobalDataType& globalData)
{
//if Traits<GlobalDataType, T>::SpecialMethodAvailable
// call the method
//else
// do something different
}
};
I tried to realize this using std::enable_if. My solution works with the Visual C 14.1 compiler but not with GCC. Here is what I tried:
template <typename T, typename Enable = void>
struct Worker
{
static void DoSomething(T t, GlobalDataType& globalData)
{
std::cout << "There is no special method (called with " << t << ")" << std::endl;
}
};
template <typename T>
struct Worker<T, typename std::enable_if<Traits<GlobalDataType, T>::SpecialMethodAvailable>::type>
{
static void DoSomething(T t, GlobalDataType& globalData)
{
globalData.SpecialMethod(t);
}
};
I used this as follows:
typedef ... GlobalDataType; //before the template declarations
int main()
{
GlobalDataType td;
int integer = 0;
Worker<int>::DoSomething(integer, td);
}
If GlobalDataType is typedef'ed to ClassWithSpecialMethod, both VS and GCC compile fine and output correctly:
Special Method called with 0
However, if GlobalDataType is typedef'ed to something that does not allow the special method (e.g. int), VS still produces the correct output while GCC results in a compile error:
In static member function ‘static void Worker::SpecialMethodAvailable>::type>::DoSomething(T, GlobalDataType&)’:
source.cpp:38:15: error: request for member ‘SpecialMethod’ in ‘globalData’, which is of non-class type
GlobalDataType {aka int}’
Can someone explain why this does not work as intended under GCC? What would be alternatives?
Link to online compiler
As explained by Jarod42, this method
static void DoSomething(T t, GlobalDataType& globalData)
{
globalData.SpecialMethod(t);
}
with GlobalDataType fixed as int, is ever wrong (for ever T type) because it's sure that int is without SpecialMethod().
To solve this problem with a minimum code change, you can templatize the second parameter
template <typename U>
static void DoSomething(T t, U & globalData)
{ globalData.SpecialMethod(t); }
If you want that DoSomething() receive (as second parameter) only a GlobalDataType, you can impose it enabling DoSomething using SFINAE, only if U is GlobalDataType. Something as
template <typename U>
static typename std::enable_if<std::is_same<U, GlobalDataType>{}>
DoSomething(T t, U & globalData)
{ globalData.SpecialMethod(t); }
What would be alternatives?
I propose you a completely different way, based (following the std::declval() example) over declaration of functions.
First of all, a couple of template helper functions
template <typename ObjectType, typename ... Args>
constexpr auto withSpecialMethodHelper (int)
-> decltype(std::declval<ObjectType>.SpecialMethod(std::declval<Args>...),
std::true_type{} );
template <typename ... Args>
constexpr std::false_type withSpecialMethodHelper (long);
Now you can write the declaration of a template function that return std::true_type if ObjectType has a SpecialMethod() that can be called with a variadic list of arguments of type Args...
template <typename ObjectType, typename ... Args>
constexpr auto withSpecialMethod ()
-> decltype( withSpecialMethodHelper<ObjectType, Args...>(0) );
or maybe better, as suggested by Jarod42, through using
template <typename ObjectType, typename ... Args>
using withSpecialMethod
= decltype( withSpecialMethodHelper<ObjectType, Args...>(0) );
If you can use C++14, you can also define a withSpecialMethod_v template constexpr variable
template <typename ObjectType, typename ... Args>
constexpr bool withSpecialMethod_v
= decltype(withSpecialMethod<ObjectType, Args...>())::value;
in case of declared function or
template <typename ObjectType, typename ... Args>
constexpr bool withSpecialMethod_v
= withSpecialMethod<ObjectType, Args...>::value;
in case of using, that can simplify the use.
Now the Worker class and specialization become
template <typename T, bool = withSpecialMethod_v<GlobalDataType, T>>
struct Worker
{
static void DoSomething (T t, GlobalDataType & globalData)
{
std::cout << "There is no special method (called with " << t << ")"
<< std::endl;
}
};
template <typename T>
struct Worker<T, true>
{
template <typename U>
static void DoSomething(T t, U & globalData)
{ globalData.SpecialMethod(t); }
};
Mvsc 14 doesn't do the 2 phases look-up needed for template.
gcc does (and is correct).
globalData.SpecialMethod(t); is incorrect for any t immediatly so the error. (globalData.SpecialMethod is incorrect and doesn't depend of template parameter).
By post-pone the evaluation you might have what you want:
template <typename T>
struct Worker<T, std::enable_if_t<Traits<GlobalDataType, T>::SpecialMethodAvailable>>
{
template <typename G, typename U>
static void f(G& g, U& u)
{
g.SpecialMethod(u);
}
static void DoSomething(T t, GlobalDataType& globalData)
{
f(globalData, t);
}
};
Demo
I am writing a template-based class for Polynomials. (Evaluation, some operations between Polynomials, differentiation, ...), like this:
template <typename _ty> class Polynomial{...
For the to_string function (and the std::ostream-left-shift-override), I had to check, if _ty supports the <-operator (i.e. yes for real numbers, no for complex numbers), so that the string could be formatted nicely. For this I use this code:
#include <type_traits>
template <class _op, class... _ty, typename = decltype(std::declval<_op>()(std::declval<_ty>()...))>
std::true_type impl_test(const _op&, const _ty&...) { return std::true_type(); }
std::false_type impl_test(...) { return std::false_type(); }
template <class> struct supports;
template <class _op, class... _ty> struct supports<_op(_ty...)> : decltype(impl_test(std::declval<_op>(), std::declval<_ty>()...)){};
#define is_ineq_supported(type) supports<std::less<>(type, type)>()
In short, is_ineq_supported(type) returns an std::true_type if there is a valid overload for the <-operator, and a false_type if not. The corresponding functions can then be called with a true_type or false_type as the distinguishing argument, like this:
template <typename _ty> void do_stuff(_ty arg, const std::true_type&) {
// do stuff with the '<'-operator
}
template <typename _ty> void do_stuff(_ty arg, const std::false_type&) {
// do stuff without the '<'-operator
}
template <typename _ty> void do_stuff(_ty arg) {
do_stuff(arg, is_ineq_supported(_ty));
}
I also have a Vector class, that overloads the binary *-operator with the dot product, so it returns a double. But for a polynomial, it only makes sense, to have coefficients and arguments, which return the same type when multiplied with one another.
My problem is the following: I'd like to have a way of checking, if the given operation returns a specified type. (maybe a similar macro?) I think, the simplest would be something that returns a true_type if the result type matches the argument type and a false_type otherwise. Of course, more general solutions are even better.
In case, the IDE and Compiler matter: I'm using Visual Studio 2015 with default settings.
Here is a generic, cross-platform solution using fit::is_callable, which will be eventually added to Boost. Also keep your eye out for std::is_callable in C++17.
No macros necessary:
#include <type_traits>
#include <iostream>
#include <fit/is_callable.hpp>
// std::less doesn't SFINAE, so we make our own test
struct less_test {
template<typename L, typename R>
auto operator()(L l, R r) -> decltype(l < r);
};
template<typename T>
using is_less_than_comparable = fit::is_callable<less_test, T, T>;
// operator< version (replace with your implementation)
template <typename T> constexpr auto
do_stuff(T arg, const std::true_type&) {
return std::integral_constant<int, 0>{};
}
// other version (replace with your implementation)
template <typename T> constexpr auto
do_stuff(T arg, const std::false_type&) {
return std::integral_constant<int, 1>{};
}
template <typename T> constexpr auto
do_stuff(T arg) {
return do_stuff(arg, is_less_than_comparable<T>{});
}
struct foo {};
int main() {
//is not less-than comparable
static_assert(do_stuff(foo{}) == 1, "");
//is less-than comparable
static_assert(do_stuff(0) == 0, "");
}
You should probably reconsider your member method detectors and use the most C++11-ish void_t based solution.
That said, if I had to update your solution, probably the following code is a viable approach:
template <class _op, class _ret, class... _ty>
typename std::enable_if<std::is_same<decltype(std::declval<_op>()(std::declval<_ty>()...)), _ret>::value, std::true_type>::type
impl_test(const _op&, const _ty&...) {
return std::true_type();
}
This will work as it follows:
struct S { int operator()() { return 42; } };
int main() {
assert((impl_test<S, int>(S{})));
// this will give you an error at compile time
// assert((impl_test<S, double>(S{})));
}
Below SFINAE code with variadic templates compiles nicely using clang 3.7.1, C++14:
#include <array>
#include <iostream>
#include <vector>
#include <cstdint>
enum class Bar : uint8_t {
ay, bee, see
};
struct S {
static void foo() {}
// std::begin(h) is defined for h of type H
template<typename H, typename... T>
static typename std::enable_if<std::is_pointer<decltype(std::begin(std::declval<H>()))*>::value>::type
foo(const H&, T&&... t)
{ std::cout << "container\n"; foo(std::forward<T>(t)...); }
// H is integral
template<typename H, typename... T>
static typename std::enable_if<std::is_integral<typename std::remove_reference<H>::type>::value>::type
foo(const H&, T&&... t)
{ std::cout << "integer\n"; foo(std::forward<T>(t)...); }
// H is an enum with underlying type = uint8_t
/*
template<typename H, typename... T>
static typename std::enable_if<std::is_same<typename std::underlying_type<H>::type,uint8_t>::value>::type
foo(const H&, T&&... t)
{ std::cout << "enum\n"; foo(std::forward<T>(t)...); }
*/
};
int main()
{
S::foo(std::array<int,8>(), 5, 5L, std::vector<int>{}, 5L);
}
I want the correct overload of foo to be called recursively, based on the type H:
if std::begin(h) is defined for an h of type H, I want the
overload number 1 to be chosen
if H is an "integral type", I want overload number 2.
This works as it is. But if I add another overload for enum types (you can try to un-comment the third overload), then I get:
error: only enumeration types have underlying types
I agree that only enums got an underlying type, hence why is Not the third overload (with std::underlying_type) get SFINAE-d away?
std::underlying_type is not SFINAE friendly. Attempting to access std::underlying_type<T>::type for a non-enumeration type results in undefined behavior (often a hard error), not substitution failure.
You need to ascertain that the type at issue is an enumeration type first, before attempting to access its underlying type. Writing this in line would be something along the lines of typename std::enable_if<std::is_enum<H>::value, std::underlying_type<H>>::type::type. Replacing the typename std::underlying_type<H>::type in your return type with this hideous mess and you get an even more hideous mess that works :)
If you find yourself needing to do this often - or just don't want to write typename std::enable_if<std::is_same<typename std::enable_if<std::is_enum<H>::value, std::underlying_type<H>>::type::type, uint8_t>::value>::type - you can write a SFINAE-friendly underlying_type:
template<class T, bool = std::is_enum<T>::value>
struct safe_underlying_type : std::underlying_type<T> {};
template<class T>
struct safe_underlying_type<T, false /* is_enum */> {};
Here's a solution inspired from T.C.'s solution that worked for my use case:
template <typename T, bool = std::is_enum<T>::value>
struct relaxed_underlying_type {
using type = typename std::underlying_type<T>::type;
};
template <typename T>
struct relaxed_underlying_type<T, false> {
using type = T;
};
Example Usage:
template <typename T>
struct UnwrapEnum {
using type =
typename std::conditional<
std::is_enum<T>::value,
typename relaxed_underlying_type<T>::type,
T>
::type;
};
enum class MyEnum : int {};
class MyClass {};
int main() {
UnwrapEnum<MyEnum>::type x;
static_assert(std::is_same<decltype(x), int>::value);
UnwrapEnum<MyClass>::type y;
static_assert(std::is_same<decltype(y), MyClass>::value);
return 0;
}
This question already has answers here:
Find out whether a C++ object is callable
(8 answers)
Closed 7 years ago.
I use the following way, presented in a stackoverflow answer to determine whether a type is callable or not:
template <typename T>
struct is_callable
{
typedef char (& yes)[1];
typedef char (& no)[2];
// we need a template here to enable SFINAE
template <typename U>
static yes deduce(char (*)[sizeof(&U::operator())]);
// fallback
template <typename> static no deduce(...);
static bool constexpr value = std::is_function<T>::value || (sizeof(deduce<T>(0)) == sizeof(yes));
};
However it fails when a class has more than one overload of operator():
#include <iostream>
template <typename T>
class A
{
T operator()();
T operator()(T a);
T operator()(T a, T b);
};
std::cout << is_callable<A<int>>::value; // false
Is it possible to determine whether a type has any variant (templated or not, one or multiple) of overloaded operator()?
First of all you need to declare A as a full type (A<int> for example).
It's not relyable to check if an object is invokeable or not just through unwrapping and comparing it's operator() because it will fail on templated or overloaded functors.
Im using the following piece of code
to detect if an object is callable with a given function signature (ReturnType(Arguments...))
This works also for overloaded or templated operator()'s since the code tries to invoke the object with the given parameters
instead of comparing the signature of it's unwrapped operator().
As small addition the object can also be checked if it is invokeable from a const, volatile or rvalue context.
#include <type_traits>
template<typename Fn>
struct impl_is_callable_with_qualifiers;
template<typename ReturnType, typename... Args>
struct impl_is_callable_with_qualifiers<ReturnType(Args...)>
{
template<typename T>
static auto test(int)
-> typename std::is_convertible<
decltype(std::declval<T>()(std::declval<Args>()...)),
ReturnType
>;
template<typename T>
static auto test(...)
-> std::false_type;
};
template<bool Condition, typename T>
using add_const_if_t = typename std::conditional<
Condition,
typename std::add_const<T>::type,
T
>::type;
template<bool Condition, typename T>
using add_volatile_if_t = typename std::conditional<
Condition,
typename std::add_volatile<T>::type,
T
>::type;
template<bool Condition, typename T>
using add_lvalue_if_t = typename std::conditional<
Condition,
typename std::add_lvalue_reference<T>::type,
T
>::type;
template<typename T, typename Fn, bool Const, bool Volatile, bool RValue>
using is_callable_with_qualifiers = decltype(impl_is_callable_with_qualifiers<Fn>::template test<
add_lvalue_if_t<!RValue,
add_volatile_if_t<Volatile,
add_const_if_t<Const,
typename std::decay<T>::type>>>
>(0));
It is not possible to determine whether a type has any variant of overloaded operator(),
however you can test for a specific type or perform tests for multiple types to detect "possible" templates.
An example which covers the code from your question would be:
#include <iostream>
template<typename T>
class A
{
public:
A() = default;
T operator()() { return T(); }
T operator()(T a) { return T(); }
T operator()(T a, T b) { return T(); }
};
template <typename TheClass, typename T>
using is_callable = is_callable_with_qualifiers<TheClass, T(T), false, false, false>;
int main()
{
std::cout << is_callable<A<int>, int>::value; // true
}
Demo
If you just need to check if smth is callable - try to call it and see what happens:
template <typename T, typename RetType = decltype(std::declval<T>()())>
constexpr bool check_is_callable(int)
{
return true;
}
template <typename>
constexpr bool check_is_callable(...)
{
return false;
}
template <typename T>
constexpr bool is_callable()
{
return check_is_callable<T>(0);
}
Here std::declval<T>() is getting the object of T type, std::declval<T>()() is attempt to call the object of T type, and if it succeeds, RetType = decltype(std::declval<T>()()) gets the return type of call. If it does not, SFIANE lets drop to the other function overload.
I'm trying to check whether a functor is compatible with a given set of parametertypes and a given return type (that is, the given parametertypes can be implicitely converted to the actual parametertypes and the other way around for the return type). Currently I use the following code for this:
template<typename T, typename R, template<typename U, typename V> class Comparer>
struct check_type
{ enum {value = Comparer<T, R>::value}; };
template<typename T, typename Return, typename... Args>
struct is_functor_compatible
{
struct base: public T
{
using T::operator();
std::false_type operator()(...)const;
};
enum {value = check_type<decltype(std::declval<base>()(std::declval<Args>()...)), Return, std::is_convertible>::value};
};
check_type<T, V, Comparer>
This works quite nicely in the majority of cases, however it fails to compile when I'm testing parameterless functors like struct foo{ int operator()() const;};, beccause in that case the two operator() of base are apperently ambigous, leading to something like this:
error: call of '(is_functor_compatible<foo, void>::base) ()' is ambiguous
note: candidates are:
note: std::false_type is_functor_compatible<T, Return, Args>::base::operator()(...) const [with T = foo, Return = void, Args = {}, std::false_type = std::integral_constant<bool, false>]
note: int foo::operator()() const
So obvoiusly I need a different way to check this for parameterless functors. I tried making a partial specialization of is_functor_compatible for an empty parameterpack, where I check if the type of &T::operator() is a parameterless memberfunction, which works more or less. However this approach obviously fails when the tested functor has several operator().
Therefore my question is if there is a better way to test for the existence of a parameterless operator() and how to do it.
When I want to test if a given expression is valid for a type, I use a structure similar to this one:
template <typename T>
struct is_callable_without_parameters {
private:
template <typename T1>
static decltype(std::declval<T1>()(), void(), 0) test(int);
template <typename>
static void test(...);
public:
enum { value = !std::is_void<decltype(test<T>(0))>::value };
};
Have you tried something like:
template<size_t>
class Discrim
{
};
template<typename T>
std::true_type hasFunctionCallOper( T*, Discrim<sizeof(T()())>* );
template<typename T>
std::false_type hasFunctionCallOper( T*, ... );
After, you discriminate on the return type of
hasFunctionCallOper((T*)0, 0).
EDITED (thanks to the suggestion of R. Martinho Fernandes):
Here's the code that works:
template<size_t n>
class CallOpDiscrim {};
template<typename T>
TrueType hasCallOp( T*, CallOpDiscrim< sizeof( (*((T const*)0))(), 1 ) > const* );
template<typename T>
FalseType hasCallOp( T* ... );
template<typename T, bool hasCallOp>
class TestImpl;
template<typename T>
class TestImpl<T, false>
{
public:
void doTellIt() { std::cout << typeid(T).name() << " does not have operator()" << std::endl; }
};
template<typename T>
class TestImpl<T, true>
{
public:
void doTellIt() { std::cout << typeid(T).name() << " has operator()" << std::endl; }
};
template<typename T>
class Test : private TestImpl<T, sizeof(hasCallOp<T>(0, 0)) == sizeof(TrueType)>
{
public:
void tellIt() { this->doTellIt(); }
};