Detecting a member function in a class which uses CRTP - c++

I am trying to customize a base classes' implementation based on the functions available in a child class using CRTP.
Basic idea of what I want:
// has_inc_function<Child, void> should detect the presence of a member function void Child::inc()
template<class Child, bool = has_inc_function<Child, void>::value>
struct base
{
// ... base implementation stuff
};
template<class Child>
struct base<Child, true>
{
// ... base specialization implementation stuff
};
struct empty : public base<empty>
{};
struct has_inc
{
void inc()
{}
};
struct has_inc_and_crtp : public base<has_inc_and_crtp>
{
void inc()
{}
};
struct has_inc_and_misuse_crtp : public base<has_inc_and_misuse_crtp, true>
{
void inc()
{}
};
struct has_inc_and_misuse_crtp2 : public base<has_inc_and_misuse_crtp, false>
{
void inc()
{}
};
struct no_inc_and_misuse_crtp : public base<no_inc_and_misuse_crtp, true>
{
};
int main()
{
static_assert(has_inc_function<empty, void>::value == false, "");
static_assert(has_inc_function<has_inc, void>::value == true, "");
static_assert(has_inc_function<has_inc_and_crtp, void>::value == true, "");
static_assert(has_inc_function<has_inc_and_misuse_crtp, void>::value == true, "");
static_assert(has_inc_function<has_inc_and_misuse_crtp2, void>::value == true, "");
static_assert(has_inc_function<no_inc_and_misuse_crtp, void>::value == false, "");
}
I've tried a variety of different implementations for has_inc_function<Child, void>, but all of them seem to fail on the case has_inc_and_crtp, and I can't figure out why. I tested with several different compilers via Compiler Explorer, and they all seem to give the same results.
How would I implement has_inc_function so that it works as I would expect in all these test case, or is what I want just not possible?
Implementations I've tried
jrok's solution (Compiler Explorer link):
template <class C, class Ret>
struct has_increment<C, Ret>
{
private:
template <class T>
static constexpr auto check(T*) -> typename std::is_same<
decltype(std::declval<T>().inc()), Ret>::type;
template <typename> static constexpr std::false_type check(...);
typedef decltype(check<C>(nullptr)) type;
public:
static constexpr bool value = type::value;
};
TartanLlama's solution (Compiler Explorer link):
note: that is implementation doesn't match the return type. I've also included sample implementations of stuff in Library fundamentals TS v2 to make this work in C++14
struct nonesuch
{
~nonesuch() = delete;
nonesuch(nonesuch const&) = delete;
void operator=(nonesuch const&) = delete;
};
namespace detail {
template <class Default, class AlwaysVoid,
template<class...> class Op, class... Args>
struct detector {
using value_t = std::false_type;
using type = Default;
};
template <class Default, template<class...> class Op, class... Args>
struct detector<Default, std::void_t<Op<Args...>>, Op, Args...> {
using value_t = std::true_type;
using type = Op<Args...>;
};
} // namespace detail
template <template<class...> class Op, class... Args>
using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;
template <template<class...> class Op, class... Args>
using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;
template <class Default, template<class...> class Op, class... Args>
using detected_or = detail::detector<Default, void, Op, Args...>;
template<class...> struct disjunction : std::false_type { };
template<class B1> struct disjunction<B1> : B1 { };
template<class B1, class... Bn>
struct disjunction<B1, Bn...>
: std::conditional_t<bool(B1::value), B1, disjunction<Bn...>> { };
template <typename T>
using has_type_t = typename T::inc;
template <typename T>
using has_non_type_t = decltype(&T::inc);
template <typename T, class RetType>
using has_inc_function =
disjunction<is_detected<has_type_t, T>, is_detected<has_non_type_t, T>>;
Valentin Milea's solution (Compiler Explorer Link):
template <class C, class RetType>
class has_inc_function
{
template <class T>
static std::true_type testSignature(RetType (T::*)());
template <class T>
static decltype(testSignature(&T::inc)) test(std::nullptr_t);
template <class T>
static std::false_type test(...);
public:
using type = decltype(test<C>(nullptr));
static const bool value = type::value;
};
Boost TTI (I couldn't figure out how to get Boost to work with Compiler Explorer):
#include <boost/tti/has_member_function.hpp>
BOOST_TTI_TRAIT_HAS_MEMBER_FUNCTION(has_inc_function, inc);

What you want is in this form plainly not possible. The parent of a class has to be known before the class is complete, and hence before it is known whether the class has such a member function or not.
What you can do is a bit dependent on how different the different instantiations of base are. If they are basically the same interface with different implementation details, you can write another class that has the same interface and a variant member (std::variant is sadly C++17, but you could do the same with dynamic polymorphism) to which all calls are forwarded. Then the decision which to use can be done when instantiating.
You could also try something in this direction:
#include <type_traits>
#include <iostream>
template<class Child>
struct base {
int foo();
};
struct has_inc: base<has_inc> {
void inc();
};
struct has_not_inc: base<has_not_inc> {
};
template<class Child, class = std::void_t<decltype(std::declval<Child>().inc())>>
struct mock {
int foo(base<Child>*) { return 1;}
};
template<class Child>
struct mock<Child> {
int foo(base<Child>*) { return 0;}
};
template<class Child>
int base<Child>::foo() {
return mock<Child,void>().foo(this);
}
int main() {
has_inc h;
has_not_inc n;
std::cout << h.foo() << " " << n.foo() << '\n';
}
Here you only use the complete child of type in the definition, not in the declaration. To the point of the definition, the complete child is available, which it was not during declaration.
There are also other ways (I think, everything is not that easy) and what you can use really depends on your use-case, I would think.
PS: std::void_t is C++17, but it is only template<class...> using void_t = void;.

I've tried a variety of different implementations for has_inc_function<Child, void>, but all of them seem to fail on the case has_inc_and_crtp, and I can't figure out why.
The problem (if I understand correctly) is that, in the has_inc_and_crpt case, the value of has_inc_function is first evaluated to determine the default value for the Childs second template parameter
template<class Child, bool = has_inc_function<Child, void>::value>
struct base
that is when Child (that is has_inc_and_crpt) is still incomplete, so the value if false, and in the following use
static_assert(has_inc_function<has_inc_and_crtp, void>::value == true, "");
remain false.
How would I implement has_inc_function so that it works as I would expect in all these test case, or is what I want just not possible?
A quick and dirty solution could be add an additional dummy defaulted template parameter to has_inc_function.
By example
// ................................VVVVVVV dummy and defaulted
template <typename C, typename RT, int = 0>
struct has_inc_function
then use it in base explicating a special (different from the default) parameter
// ........................................................V different from the default
template<class Child, bool = has_inc_function<Child, void, 1>::value>
struct base
So, when you use has_inc_functin in the static assert,
static_assert(has_inc_function<has_inc_and_crtp, void>::value == true, "");
the class is different, is evaluated in that moment and has_inc_and_crpt is detected with inc() method.
But this only resolve the problem at test case (static_assert()) level.
Still remain the problem (a problem that I don't how to solve) that, declaring base, the default value remain false. So (I suppose) has_inc_and_crpt still select the wrong base base.
The following is a full compiling example, following the jrok's solution.
#include <type_traits>
template <typename C, typename RT, int = 0>
struct has_inc_function
{
private:
template <typename T>
static constexpr auto check(T *) ->
typename std::is_same<decltype(std::declval<T>().inc()), RT>::type;
template <typename>
static constexpr std::false_type check(...);
using type = decltype(check<C>(nullptr));
public:
/// #brief True if there is an inc member function
static constexpr bool value = type::value;
};
template <typename Child, bool = has_inc_function<Child, void, 1>::value>
struct base
{ };
template <typename Child>
struct base<Child, true>
{ };
struct empty : public base<empty>
{ };
struct has_inc
{ void inc() {} };
struct has_inc_and_crtp : public base<has_inc_and_crtp>
{ void inc() {} };
struct has_inc_and_misuse_crtp : public base<has_inc_and_misuse_crtp, true>
{ void inc() {} };
struct has_inc_and_misuse_crtp2 : public base<has_inc_and_misuse_crtp, false>
{ void inc() {} };
struct no_inc_and_misuse_crtp : public base<no_inc_and_misuse_crtp, true>
{ };
template <typename C, typename RT>
constexpr auto hif_v = has_inc_function<C, RT>::value;
int main ()
{
static_assert(hif_v<empty, void> == false, "");
static_assert(hif_v<has_inc, void> == true, "");
static_assert(hif_v<has_inc_and_crtp, void> == true, "");
static_assert(hif_v<has_inc_and_misuse_crtp, void> == true, "");
static_assert(hif_v<has_inc_and_misuse_crtp2, void> == true, "");
static_assert(hif_v<no_inc_and_misuse_crtp, void> == false, "");
}

Related

Can you "hop" between "linked classes" in C++ metaprogramming?

Suppose you have something like this:
template<class D>
class HasDef {
public:
typedef D Def;
};
class A : public HasDef<class B> {};
class B : public HasDef<class C> {};
class C {};
So it is like a "metaprogramming linked list", with type links, via the included typedef Def. Now I want to make a template "Leaf" that, when applied to A, follows the links to yield C:
void f() {
Leaf<A>::type v; // has type C
}
Is it even possible at all to do this? I've tried some methods with std::compare and similar, but none are valid code: everything seems to run into issues with either that C has no Def typedef, or else that the type Leaf<> itself is incomplete when the inner recursive "call" is made so it (or its internal type type) cannot be referenced.
FWIW, the reason I want this is for making a "hierarchical state machine" where that Def represents the default state for each state in the hierarchy, and something a bit more elaborate that this seems to provide a fairly neat and clean "user interface syntax" for it.
I don't really like f(...) in modern code, thus my version uses void_t from C++17:
#include <type_traits>
template<class D>
struct HasDef {
typedef D Def;
};
struct A : HasDef<class B> {};
struct B : HasDef<class C> {};
struct C {};
template <typename T, typename=void>
struct DefPresent : std::false_type{};
template <typename T>
struct DefPresent<T, std::void_t<typename T::Def>> : std::true_type{};
template<typename T, bool deeper = DefPresent<T>::value>
struct Leaf
{
using Type = typename Leaf<typename T::Def>::Type;
};
template<typename T>
struct Leaf<T, false >
{
typedef T Type;
};
static_assert(std::is_same<typename Leaf<C>::Type, C>::value, "C");
static_assert(std::is_same<typename Leaf<B>::Type, C>::value, "B");
static_assert(std::is_same<typename Leaf<A>::Type, C>::value, "A");
https://godbolt.org/z/5h5rfe81o
EDIT: just for completenes, 2 C++20 variants utilizing concepts. Tested on GCC 10
#include <type_traits>
#include <concepts>
template<class D>
struct HasDef {
typedef D Def;
};
struct A : HasDef<class B> {};
struct B : HasDef<class C> {};
struct C {};
template <typename T>
concept DefPresent = requires(T a)
{
typename T::Def;
};
template<typename T>
struct Leaf
{
using Type = T;
};
template<typename T>
requires DefPresent<T>
struct Leaf<T>
{
using Type = Leaf<typename T::Def>::Type;
};
static_assert(std::is_same_v<typename Leaf<C>::Type, C>, "C");
static_assert(std::is_same_v<typename Leaf<B>::Type, C>, "B");
static_assert(std::is_same_v<typename Leaf<A>::Type, C>, "A");
template<typename T>
struct Leaf2
{
template <typename M>
static M test(M&&);
template <DefPresent M>
static auto test(M&&) -> typename Leaf2<typename M::Def>::Type;
using Type = decltype(test(std::declval<T>()));
};
static_assert(std::is_same<typename Leaf2<C>::Type, C>::value, "C");
static_assert(std::is_same<typename Leaf2<B>::Type, C>::value, "B");
static_assert(std::is_same<typename Leaf2<A>::Type, C>::value, "A");
https://godbolt.org/z/vcqEaPrja
You can implement this with SFINAE to separate types that have the typedef from ones that don't when trying to follow them to their leaf. I used the trick from this SO answer here.
The first implementation for Leaf follows the typedef recursively, the second one just defines the type itself as there is no typedef to follow.
Also note I changed your classes to struct for default-public inheritance. Also I changed the order of their definitions for this to compile.
Compiler explorer
#include <type_traits>
namespace detail
{
template<typename T> struct contains_def {
template<typename U> static char (&test(typename U::Def const*))[1];
template<typename U> static char (&test(...))[2];
static const bool value = (sizeof(test<T>(0)) == 1);
};
template<typename T, bool has = contains_def<T>::value>
struct Leaf {
using type = typename Leaf<typename T::Def>::type;
};
template<typename T>
struct Leaf<T, false> {
using type = T;
};
}
template <typename T>
using Leaf = detail::Leaf<T>; // expose Leaf to the global scope
template <typename T>
using Leaf_t = typename Leaf<T>::type; // for convenience
template<typename T>
struct AddDef {
using Def = T;
};
struct C {};
struct B : AddDef<C> {};
struct A : AddDef<B> {};
static_assert(std::is_same_v<Leaf_t<A>, C>);
static_assert(std::is_same_v<Leaf_t<B>, C>);
static_assert(std::is_same_v<Leaf_t<C>, C>);
You could make it a type trait:
#include <utility> // declval
template<class L>
struct Leaf {
template<class M>
static L test(const M&); // match L's without Def
template<class M> // match L's with Def
static auto test(M&&) -> typename Leaf<typename M::Def>::type;
// find matching test() overload, prefer test(M&&):
using type = decltype( test(std::declval<L>()) );
};
template<class L> // bonus helper types
using Leaf_t = typename Leaf<L>::type;
Demo and template resolution # cppinsights

How do I declare SFINAE class?

Something is not working quite well for me. Is this the way to declare a class, that accepts only floating point template parameter?
template <typename T, swift::enable_if<std::is_floating_point<T>::value> = nullptr>
class my_float;
I fail to define methods outside this class. Doesn't compile, not sure why
Well... not exactly SFINAE... but maybe, using template specialization? Something as follows ?
template <typename T, bool = std::is_floating_point<T>::value>
class my_float;
template <typename T>
class my_float<T, true>
{
// ...
};
If you really want use SFINAE, you can write
template <typename T,
typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
class my_float
{
// ...
};
or also (observe the pointer there isn't in your example)
template <typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
class my_float // ------------------------------------------------^
{
};
-- EDIT --
As suggested by Yakk (thanks!), you can mix SFINAE and template specialization to develop different version of your class for different groups of types.
By example, the following my_class
template <typename T, typename = void>
class my_class;
template <typename T>
class my_class<T,
typename std::enable_if<std::is_floating_point<T>::value>::type>
{
// ...
};
template <typename T>
class my_class<T,
typename std::enable_if<std::is_integral<T>::value>::type>
{
// ...
};
is developed for in two versions (two different partial specializations), the first one for floating point types, the second one for integral types. And can be easily extended.
You can also use static_assert to poison invalid types.
template <typename T>
class my_float {
static_assert(std::is_floating_point<T>::value,
"T is not a floating point type");
// . . .
};
It's a little bit more direct, in my opinion.
With either of the other approaches, e.g.
template <typename T, bool = std::is_floating_point<T>::value>
class my_float;
template <typename T> class my_float<T, true> { /* . . . */ };
my_float<int,true> is a valid type. I'm not saying that that's a bad approach, but if you want to avoid this, you'll have to encapsulate
my_float<typename,bool> within another template, to avoid exposing the bool template parameter.
indeed, something like this worked for me (thanks to SU3's answer).
template<typename T, bool B = false>
struct enable_if {};
template<typename T>
struct enable_if<T, true> {
static const bool value = true;
};
template<typename T, bool b = enable_if<T,is_allowed<T>::value>::value >
class Timer{ void start(); };
template<typename T, bool b>
void Timer<T,b>::start()
{ \* *** \*}
I am posting this answer because I did not want to use partial specialization, but only define the behavior of the class outside.
a complete workable example:
typedef std::integral_constant<bool, true> true_type;
typedef std::integral_constant<bool, false> false_type;
struct Time_unit {
};
struct time_unit_seconds : public Time_unit {
using type = std::chrono::seconds;
};
struct time_unit_micro : public Time_unit {
using type = std::chrono::microseconds;
};
template<typename T, bool B = false>
struct enable_if {
};
template<typename T>
struct enable_if<T, true> {
const static bool value = true;
};
template<typename T,
bool b = enable_if<T,
std::is_base_of<Time_unit,
T>::value
>::value>
struct Timer {
int start();
};
template<typename T, bool b>
int Timer<T, b>::start() { return 1; }
int main() {
Timer<time_unit_seconds> t;
Timer<time_unit_micro> t2;
// Timer<double> t3; does not work !
return 0;
}

Define a templete class only using bool and char

I intended to have a class that only specifically dealing with bool or char type without using boost. My code is as below and I am using VS2017 Community:
#include <type_traits>
template<typename T,
typename std::enable_if_t<
std::is_same<T, bool>::value || std::is_same<T, char>::value >::type >
class BoolAndCharData
{
public:
BoolAndCharData(const T& _data) {...}
void DoSomething() {...}
}; // end of class BoolAndCharData
int main()
{
char c;
BoolAndCharData<char> data(c); // error C2976: too few template parameter
....
} // end of main()
I tried the other way as someone presented in this site, compiler said it couldn't recognize the template:
template<typename T>
class BoolAndCharData<T,
typename std::enable_if_t< std::is_same<T, bool>::value ||
std::is_same<T, char>::value >::type >
{
public:
BoolAndCharData(const T& _data) {...}
void DoSomething() {...}
}; // end of class BoolAndCharData
I have browsed through this site and other webs for a few hours and found although there are quite some discussions over restricting template type, most either using boots or specific for a function. I still can't get it clear how to write a template class with selected data type. Can someone kindly point the way to rescue me from blindly trying ?
You don't need to use std::enable_if_t to do that. A static_assert is enough in this case.
As a minimal, working example:
#include <type_traits>
template<typename T>
class BoolAndCharData {
static_assert(std::is_same<T, bool>::value or std::is_same<T, char>::value, "!");
public:
BoolAndCharData(const T& _data) {}
void DoSomething() {}
};
int main() {
char c;
BoolAndCharData<char> d1(c);
// the following line won't compile
// BoolAndCharData<int> d2(0);
// ...
}
See it on Coliru. Errors when using static_assert are also nicer than what you get out usually from templates.
A possible solution is the following
template <typename, typename = void>
class BoolAndCharData;
template <typename T>
class BoolAndCharData<T, std::enable_if_t<
std::is_same<T, bool>::value || std::is_same<T, char>::value > >
{
public:
BoolAndCharData(const T& _data)
{}
void DoSomething()
{}
};
A little variation on the theme is define a specific type-traits
template <typename, typename = void>
struct boolOrChar
{ };
template <typename T>
struct boolOrChar<bool, T>
{ using type = T; };
template <typename T>
struct boolOrChar<char, T>
{ using type = T; };
so BoolAndCharData can be written as
template <typename, typename = void>
class BoolAndCharData;
template <typename T>
class BoolAndCharData<T, typename boolOrChar<T>::type>
{
public:
BoolAndCharData(const T& _data)
{}
void DoSomething()
{}
};
You can use template specialization, together with inheritance for the common code:
// Declares the generic case
template<typename T>
struct BoolAndCharData;
// Common base-class for the common code
template<typename T>
struct BoolAndCharDataCommon
{
explicit BoolAndCharDataCommon(T) {}
void DoSomething() {}
};
// Specialization for the char data-type
template<>
struct BoolAndCharData<char> : public BoolAndCharDataCommon<char>
{
// To use the constructor(s) from the base class
using BoolAndCharDataCommon::BoolAndCharDataCommon;
};
// Specialization for the bool data-type
template<>
struct BoolAndCharData<bool> : public BoolAndCharDataCommon<bool>
{
// To use the constructor(s) from the base class
using BoolAndCharDataCommon::BoolAndCharDataCommon;
};
int main()
{
BoolAndCharData<char> a('a');
BoolAndCharData<bool> b(false);
a.DoSomething();
b.DoSomething();
// This will lead to a compiler error
BoolAndCharData<int> c;
}
This way makes it easy to collect all common code, but also very easy to add code that is specific for the specific types if that's needed.

How to test if a method is const?

How can I get a boolean value indicating if a known method has the const qualifier or not?
For example:
struct A {
void method() const {}
};
struct B {
void method() {}
};
bool testA = method_is_const<A::method>::value; // Should be true
bool testB = method_is_const<B::method>::value; // Should be false
In the type_traits header I found an is_const test I could use, but I need the method type, and I'm unsure how to obtain that.
I tried: std::is_const<decltype(&A::method)>::value but it doesn't work, and I can understand why (void (*ptr)() const) != const void (*ptr)()).
It is a lot simpler to check whether a member function can be called on a const-qualified lvalue.
template<class T>
using const_lvalue_callable_foo_t = decltype(std::declval<const T&>().foo());
template<class T>
using has_const_lvalue_callable_foo = std::experimental::is_detected<const_lvalue_callable_foo_t, T>;
Rinse and repeat, except with std::declval<const T>(), to check if said function can be called on a const-qualified rvalue. I can think of no good use cases for const && member functions, so whether there's a point in detecting this case is questionable.
Consult the current Library Fundamentals 2 TS working draft on how to implement is_detected.
It is a lot more convoluted to check whether a particular pointer-to-member-function type points to a function type with a particular cv-qualifier-seq. That requires 6 partial specializations per cv-qualifier-seq (const and const volatile are different cv-qualifier-seqs), and still can't handle overloaded member functions or member function templates. Sketching the idea:
template<class T>
struct is_pointer_to_const_member_function : std::false_type {};
template<class R, class T, class... Args>
struct is_pointer_to_const_member_function<R (T::*)(Args...) const> : std::true_type {};
template<class R, class T, class... Args>
struct is_pointer_to_const_member_function<R (T::*)(Args...) const &> : std::true_type {};
template<class R, class T, class... Args>
struct is_pointer_to_const_member_function<R (T::*)(Args...) const &&> : std::true_type {};
template<class R, class T, class... Args>
struct is_pointer_to_const_member_function<R (T::*)(Args..., ...) const> : std::true_type {};
template<class R, class T, class... Args>
struct is_pointer_to_const_member_function<R (T::*)(Args..., ...) const &> : std::true_type {};
template<class R, class T, class... Args>
struct is_pointer_to_const_member_function<R (T::*)(Args..., ...) const &&> : std::true_type {};
If you want const volatile to be true too, stamp out another 6 partial specializations along these lines.
The reason std::is_const<decltype(&A::method)>::value doesn't work is that a const member function isn't a const (member function). It's not a top-level const in the way that it would be for const int vs int.
What we can do instead is a type trait using void_t that tests whether we can call method on a const T:
template <typename... >
using void_t = void;
template <typename T, typename = void>
struct is_const_callable_method : std::false_type { };
template <typename T>
struct is_const_callable_method<T, void_t<
decltype(std::declval<const T&>().method())
> > : std::true_type { };
Demo
In C++20, things get a lot easier because concepts have been standardized, which subsumes the detection idiom.
Now all we need to write is this constraint:
template<class T>
concept ConstCallableMethod = requires(const T& _instance) {
{ _instance.method() }
};
ConstCallableMethod tests that the expression _instance.has_method() is well formed given that _instance is a const-reference type.
Given your two classes:
struct A {
void method() const { }
};
struct B {
void method() { }
};
The constraint will be true for A (ConstCallableMethod<A>) and false for B.
If you wish to also test that the return type of the method function is void, you can add ->void to the constraint like so:
template<class T>
concept ConstCallableMethodReturnsVoid = requires(const T& _instance) {
{ _instance.method() } -> void
};
If you wish to be a little more generic, you can pass in a member function pointer to the concept and test if that function pointer can be called with a const instance (although this gets a little less useful when you have overloads):
template<class T, class MemberF>
concept ConstCallableMemberReturnsVoid = requires(const T& _instance, MemberF _member_function) {
{ (_instance.*_member_function)() } -> void
};
You'd call it like so:
ConstCallableMemberReturnsVoid<A, decltype(&A::method)>
This allows for some other theoretical class like C, that has a const method, but it's not named method:
struct C
{
void foobar() const{}
};
And we can use the same concept to test:
ConstCallableMemberReturnsVoid<C, decltype(&C::foobar)>
Live Demo
Create a type trait to determine the const-ness of a method:
template<typename method_t>
struct is_const_method;
template<typename CClass, typename ReturnType, typename ...ArgType>
struct is_const_method< ReturnType (CClass::*)(ArgType...)>{
static constexpr bool value = false;
};
template<typename CClass, typename ReturnType, typename ...ArgType>
struct is_const_method< ReturnType (CClass::*)(ArgType) const>{
static constexpr bool value = true;
};

Howto check a type for the existence of parameterless operator()

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(); }
};