SFINAE, is callable trait - c++

I am trying to implement a little is_callable trait to improve mysel. However I am running a little issue. The issue comes when I am trying to use functor with arguments (or lambda).
I did not figure out how pass the argument types, or how to deduce them automatically... Here is my code and the results :
#include <iostream>
#include <type_traits>
#include <cstdlib>
template<typename T, typename = void>
struct is_callable_impl : std::false_type {};
template<typename R, typename ...Args>
struct is_callable_impl<R(Args...), std::void_t <R(Args...)>> : std::true_type {};
template<typename Fn>
struct is_callable_impl < Fn, std::void_t<decltype(std::declval<Fn>()())>> : std::true_type{};
struct fonctor {
void operator()() {}
};
struct fonctor2 {
void operator()(double) {}
};
int fonct();
int fonct2(double);
int main() {
auto l = [](float) {return false; };
auto l2 = [&l] {return true; };
std::cout << "expr" << std::endl;
std::cout << is_callable_impl<double()>::value << std::endl; //write 1
std::cout << is_callable_impl<int(int)>::value << std::endl; // write 1
std::cout << is_callable_impl<void(double)>::value << std::endl;// write 1
std::cout << is_callable_impl<void(double, int)>::value << std::endl; // write 1
std::cout << "lambda" << std::endl;
std::cout << is_callable_impl<decltype(l)>::value << std::endl;// write 0
std::cout << is_callable_impl<decltype(l2)>::value << std::endl;// write 1
std::cout << "function" << std::endl;
std::cout << is_callable_impl<decltype(fonct)>::value << std::endl;// write 1
std::cout << is_callable_impl<decltype(fonct2)>::value << std::endl;// write 1
std::cout << "functors" << std::endl;
std::cout << is_callable_impl<fonctor>::value << std::endl; // write 1
std::cout << is_callable_impl<fonctor2>::value << std::endl; // write 0
std::cout << "uncalled type" << std::endl;
std::cout << is_callable_impl<int>::value << std::endl;// write 0
system("pause");
return 0;
}

Okay I tested another Idea. Using std::is_function you can detect if the object is a function or an expression.
After, you just need to check if operator= exist or not. However, my implementation does not work when a functor have overloaded operator() function.
template<typename T, typename AlwaysVoid = void>
struct is_callable_impl : std::false_type {};
template<typename F>
struct is_callable_impl < F, std::enable_if_t < std::is_function<F>{}>> // Expression and Functions
: std::true_type {};
// Lambda and functor
template<typename F>
struct is_callable_impl < F, std::void_t<std::enable_if_t < !std::is_function<F>{} >, decltype(&F::operator())>> : std::true_type{};

Related

template specialization and default template parameters and sfinae

I have put together an example on creating a base template with a number of specializations.
#include <iostream>
template<typename T, typename U = void>
struct foo {
static void apply() {
std::cout << "a: " << __FUNCTION__ << std::endl;
}
};
template<typename U>
struct foo<int, U> {
static void apply() {
std::cout << "b: " << __FUNCTION__ << std::endl;
}
};
template<typename U>
struct foo<double, U> {
static void apply() {
std::cout << "c: " << __FUNCTION__ << std::endl;
}
};
template<>
struct foo<double, double> {
static void apply() {
std::cout << "d: " << __FUNCTION__ << std::endl;
}
};
template<typename T>
struct foo<T, std::enable_if_t<std::is_same_v<T, char>>> {
static void apply() {
std::cout << "e: " << __FUNCTION__ << std::endl;
}
};
template<>
struct foo<short> {
static void apply() {
std::cout << "f: " << __FUNCTION__ << std::endl;
}
};
template<>
struct foo<unsigned long, void> {
static void apply() {
std::cout << "g: " << __FUNCTION__ << std::endl;
}
};
int main() {
foo<long>::apply();
foo<long, long>::apply();
foo<int>::apply();
foo<int, int>::apply();
foo<double>::apply();
foo<double, float>::apply();
foo<double, double>::apply();
foo<char>::apply();
foo<short>::apply();
foo<unsigned long>::apply();
return 0;
}
When a specialization is defined and a template parameter is defined in the base template such as the template parameter U which is defaulted to void how is this propagated to the specializations. Is the done at the point of specialization as in the first specialization foo<int, U> and U must be void as it is unspecified and adopted from the base template?
Also with the
template<typename T>
struct foo<T, std::enable_if_t<std::is_same_v<T, char>>>
specialization, the enable_if_t yields the type void, why does this not collide with the base template and how is it considered more specialized?
Any additional quotes from the standard to complement answers are additionally welcome.

Best way to specialize function using enable_if for only some types

I have this code that specializes the print function for two types, and reverts back to the base version for any other types.
My question is, is there a way to write this without having to type out the negative case for all the specialized versions in the enable_if of the base print function?
i.e. is there a way to remove all the !std::is_same and still have an unambiguous print function?
Any versions of C++ welcome, but one that works in c++14 would be helpful.
#include <iostream>
template<typename T, std::enable_if_t<!std::is_same<T, int>::value && !std::is_same<T, double>::value, int> = 42>
void print(T data)
{
std::cout << "base:" << data << std::endl;
}
template<typename T, std::enable_if_t<std::is_same<T, double>::value, int> = 42>
void print(T data)
{
std::cout << "double:" << data << std::endl;
}
template<typename T, std::enable_if_t<std::is_same<T, int>::value, int> = 42>
void print(T data)
{
std::cout << "int:" << data << std::endl;
}
int main()
{
std::string foo("foo");
double bar = 1.2;
int baz = 5;
print(foo);
print(bar);
print(baz);
}
For your use case, you can simply provide overloads for the print function as needed
#include <iostream>
template<typename T>
void print(T data)
{
std::cout << "base:" << data << std::endl;
}
void print(double data)
{
std::cout << "double:" << data << std::endl;
}
void print(int data)
{
std::cout << "int:" << data << std::endl;
}
However, if you have more complicated constraints on T, then you can't specialize print without explicitly providing the negation of the constraints in the "default" case.
If you have access to c++17, you can write this in a single function. The usual if-else logic means that the base case is only triggered if the "specializations" are not. This avoids having to specify the negations.
template<typename T>
void print(T data)
{
if constexpr(std::is_same<T, double>{})
std::cout << "double:" << data << std::endl;
else if constexpr(std::is_same<T, int>{})
std::cout << "int:" << data << std::endl;
else // if not same as int or double
std::cout << "base:" << data << std::endl;
}
One way to avoid complementary conditions is to give an overload priority between overload:
template <std::size_t N> struct priority_overload : priority_overload<N - 1> {};
template <> struct priority_overload<0> {}; // Least priority
and then
template<typename T>
void print(T data, priority_overload<0>) // fallback
{
std::cout << "base:" << data << std::endl;
}
template<typename T, std::enable_if_t<condition1<T>::value, int> = 42>
void print(T data, priority_overload<1>)
{
std::cout << "cond1:" << data << std::endl;
}
template<typename T, std::enable_if_t<condition2<T>::value, int> = 42>
void print(T data, priority_overload<2>)
{
std::cout << "cond2:" << data << std::endl;
}
template<typename T>
void print(T data)
{
print(data, priority_overload<10>{});
// Priority should be greater or equal to the one of possible overloads
}

SFINAE matches don't go as expected

I am using both g++ 7.5.0 and clang 6.0.0 on ubuntu to try the SFINAE function of auto dispatching function call according to the method existence of an object and the result doesn't go as expected.
what I expected is that for the container of vector, it should invoke the clear method of the vector in the container's destruction function. for primitive types like int, it does nothing other than printing out messages.
but they give both the later one now. I wonder what's wrong here.
#include <iostream>
#include <typeinfo>
#include <vector>
using namespace std;
template <typename T> struct has_clear {
typedef char true_type;
typedef int false_type;
template <typename U, size_t (U::*)() const> struct SFINAE {
};
template <typename U> static char Test(SFINAE<U, &U::clear> *);
template <typename U> static int Test(...);
static const bool has_method = sizeof(Test<T>(nullptr) == sizeof(char));
typedef decltype(Test<T>(nullptr)) ret_type;
// typedef Test<T>(0) type_t;
};
template <typename T> class MyContainer {
// using typename has_clear<T>::true_type;
// using typename has_clear<T>::false_type;
T _obj;
public:
MyContainer(const T &obj) : _obj(obj) {}
// static void clear(MyContainer *m);
void clear(const typename has_clear<T>::true_type t)
{
cout << "the " << typeid(_obj).name() << " object has clear() function!" << endl;
cout << "typeid(t).name(): " << typeid(t).name() << endl;
_obj.clear();
cout << "clear has be done!" << endl;
}
void clear(const typename has_clear<T>::false_type t)
{
cout << "the " << typeid(_obj).name() << " object has no clear() function!" << endl;
cout << "typeid(t).name(): " << typeid(t).name() << endl;
cout << "just do nothing and quit!" << endl;
}
~MyContainer()
{
cout << "has_clear<T>::true_type: " << typeid(typename has_clear<T>::true_type()).name()
<< endl;
cout << "has_clear<T>::flase_type: " << typeid(typename has_clear<T>::false_type()).name()
<< endl;
clear(typename has_clear<T>::ret_type());
};
// template <bool b> ~MyContainer();
};
int main()
{
cout << "before MyContainer<vector<int>>" << endl;
{
vector<int> int_vec;
MyContainer<vector<int>> int_vec_container(int_vec);
}
cout << "after MyContainer<vector<int>>" << endl;
cout << "before MyContainer<int>" << endl;
{
MyContainer<int> int_container(1);
}
cout << "after MyContainer<int>" << endl;
}
it yields:
before MyContainer<vector<int>>
has_clear<T>::true_type: FcvE
has_clear<T>::flase_type: FivE
the St6vectorIiSaIiEE object has no clear() function!
typeid(t).name(): i
just do nothing and quit!
after MyContainer<vector<int>>
before MyContainer<int>
has_clear<T>::true_type: FcvE
has_clear<T>::flase_type: FivE
the i object has no clear() function!
typeid(t).name(): i
just do nothing and quit!
after MyContainer<int>
You have a bug in the implementation of has_clear:
template <typename U, size_t (U::*)() const> struct SFINAE {
}; // ^^^^^^^^^^^^^^^^^^^^^
std::vector::clear returns void and can't be const. So:
template <typename U, void (U::*)()> struct SFINAE {
};
I don't know what the issue is with your implementation has_clear, but it can be replaced with this greatly simplified, working implementation using more modern SFINAE/type_traits features:
template<typename T, typename Enable = void>
struct has_clear : std::false_type {};
template<typename T>
struct has_clear<
T,
std::enable_if_t<
std::is_same_v<decltype(&T::clear), void (T::*)()> ||
std::is_same_v<decltype(&T::clear), void (T::*)() noexcept>
>
> : std::true_type {};
And for convenience:
template<typename T>
constexpr bool has_clear_v = has_clear<T>::value;
Combined with if constexpr, you can very cleanly and simply decide which code path to run when others would fail to compile. For example:
template<typename T>
void maybe_clear(T t){
if constexpr (has_clear_v<T>){
// only compiled when T has a non-static clear() method
std::cout << "clearing " << typeid(T).name() << '\n';
t.clear();
} else {
// only compiled when T does not have a non-static clear() method
std::cout << "doing nothing with " << typeid(T).name() << '\n';
}
}
I believe this achieves what you want, but correct if I have misunderstood. This solution comes at the cost of requiring C++17.
Live Demo

Partial template function specialization with enable_if: make default implementation

Using C++11's enable_if I want to define several specialized implementations for a function (based on the type of the parameter, say) as well as a default implementation. What is the correct way to define it?
The following example does not work as intended since the "generic" implementation is called, whatever the type T.
#include <iostream>
template<typename T, typename Enable = void>
void dummy(T t)
{
std::cout << "Generic: " << t << std::endl;
}
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type>
void dummy(T t)
{
std::cout << "Integral: " << t << std::endl;
}
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type>
void dummy(T t)
{
std::cout << "Floating point: " << t << std::endl;
}
int main() {
dummy(5); // Print "Generic: 5"
dummy(5.); // Print "Generic: 5"
}
One solution in my minimal example consists in explicitly declaring the "generic" implementation as not for integral nor floating point types, using
std::enable_if<!std::is_integral<T>::value && !std::is_floating_point<T>::value>::type
This is exactly what I want to avoid, since in my real use cases there are a lot of specialized implementations and I would like to avoid a very long (error prone!) condition for the default implementation.
You can introduce a rank to give priority to some of your overloads:
template <unsigned int N>
struct rank : rank<N - 1> { };
template <>
struct rank<0> { };
You can then define your dummy overloads like this:
template<typename T>
void dummy(T t, rank<0>)
{
std::cout << "Generic: " << t << std::endl;
}
template<typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void dummy(T t, rank<1>)
{
std::cout << "Integral: " << t << std::endl;
}
template<typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
void dummy(T t, rank<1>)
{
std::cout << "Floating point: " << t << std::endl;
}
Then, you can hide the call behind a dispatch:
template <typename T>
void dispatch(T t)
{
return dummy(t, rank<1>{});
}
Usage:
int main()
{
dispatch(5); // Print "Integral: 5"
dispatch(5.); // Print "Floating point: 5"
dispatch("hi"); // Print "Generic: hi"
}
live example on wandbox
Explanation:
Using rank introduces "priority" because implicit conversions are required to convert a rank<X> to a rank<Y> when X > Y. dispatch first tries to call dummy with rank<1>, giving priority to your constrained overloads. If enable_if fails, rank<1> is implicitly converted to rank<0> and enters the "fallback" case.
Bonus: here's a C++17 implementation using if constexpr(...).
template<typename T>
void dummy(T t)
{
if constexpr(std::is_integral_v<T>)
{
std::cout << "Integral: " << t << std::endl;
}
else if constexpr(std::is_floating_point_v<T>)
{
std::cout << "Floating point: " << t << std::endl;
}
else
{
std::cout << "Generic: " << t << std::endl;
}
}
live example on wandbox
Function cannot be partially specialized. I assume what you want to do is to prefer those overloads which contains explicit condition? One way to achieve that is by using variadic arguments ellipsis in declaration of the default function as the ellipsis function have lower priority in overload resolution order:
#include <iostream>
template<typename T>
void dummy_impl(T t, ...)
{
std::cout << "Generic: " << t << std::endl;
}
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void dummy_impl(T t, int)
{
std::cout << "Integral: " << t << std::endl;
}
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
void dummy_impl(T t, int)
{
std::cout << "Floating point: " << t << std::endl;
}
template <class T>
void dummy(T t) {
dummy_impl(t, int{});
}
int main() {
dummy(5);
dummy(5.);
dummy("abc");
}
Output:
Integral: 5
Floating point: 5
Generic: abc
[live demo]
Another option as #doublep mention in comment is by use of structure with implementation of your function and then partially specialize it.
I would use tag dispatching like so:
namespace Details
{
namespace SupportedTypes
{
struct Integral {};
struct FloatingPoint {};
struct Generic {};
};
template <typename T, typename = void>
struct GetSupportedType
{
typedef SupportedTypes::Generic Type;
};
template <typename T>
struct GetSupportedType< T, typename std::enable_if< std::is_integral< T >::value >::type >
{
typedef SupportedTypes::Integral Type;
};
template <typename T>
struct GetSupportedType< T, typename std::enable_if< std::is_floating_point< T >::value >::type >
{
typedef SupportedTypes::FloatingPoint Type;
};
template <typename T>
void dummy(T t, SupportedTypes::Generic)
{
std::cout << "Generic: " << t << std::endl;
}
template <typename T>
void dummy(T t, SupportedTypes::Integral)
{
std::cout << "Integral: " << t << std::endl;
}
template <typename T>
void dummy(T t, SupportedTypes::FloatingPoint)
{
std::cout << "Floating point: " << t << std::endl;
}
} // namespace Details
And then hide the boiler plate code like so:
template <typename T>
void dummy(T t)
{
typedef typename Details::GetSupportedType< T >::Type SupportedType;
Details::dummy(t, SupportedType());
}
GetSupportedType gives you one central way to guess the actual type you are going to use, that's the one you want to specialize everytime you add a new type.
Then you just invoke the right dummy overload by providing an instance of the right tag.
Finally, invoke dummy:
dummy(5); // Print "Generic: 5"
dummy(5.); // Print "Floating point: 5"
dummy("lol"); // Print "Generic: lol"

Implementation of std::is_function - why my implementation behaves differently?

I have following implementation of is_function:
template <typename SomeType>
struct _is_function_helper : public _false_expression {};
template <typename ReturnType, typename ... ArgumentTypes>
struct _is_function_helper<ReturnType (ArgumentTypes ...)> : _true_expression {};
template <typename ReturnType, typename ... ArgumentTypes>
struct _is_function_helper<ReturnType (ArgumentTypes ..., ...)> : _true_expression {};
template <typename SomeType>
struct _is_function : public _boolean_expression<_is_function_helper<typename _remove_cv<typename _remove_reference<SomeType>::Type>::Type>::value> {};
I remove references, cv qualifiers and then try to inherit from same bool expression as _is_function_helper would. Then I tried following tests:
void func(int,int) { };
struct A { void foo(int); };
....
auto r = func;
std::cout << std::boolalpha;
std::cout << std::is_function<decltype(func)>::value << " " << _is_function<decltype(func)>::value << std::endl;
std::cout << std::is_function<int(int)>::value << " " << _is_function<int(int)>::value << std::endl;
std::cout << std::is_function<int(*)(int)>::value << " " << _is_function<int(*)(int)>::value << std::endl;
std::cout << std::is_function<decltype(r)>::value << " " << _is_function<decltype(r)>::value << std::endl;
std::cout << std::is_function<decltype(*r)>::value << " " << _is_function<decltype(*r)>::value << std::endl;
std::cout << std::is_function<decltype(&A::foo)>::value << " " << _is_function<decltype(&A::foo)>::value << std::endl;
And here is output of these tests:
true true
true true
false false
false false
false true
false false
I have two questions:
Why is output in 5th test case different?
How is it possible to detect member function of struct using _is_function?
The output in the 5th case is different because decltype(*r) is a reference to a function. Your implementation removes this reference, but std::is_function does not.
You can detect a member function pointer by adding a specialization like this:
template <typename ReturnType, typename ... ArgumentTypes, typename T>
struct _is_function_helper<ReturnType (T::*) (ArgumentTypes ...)>
: _true_expression {};