Can't get a method existence detection mechanism to work - c++

I'm writing templated code which needs to invoke a certain templated operator() of functors it gets - but only if that operator() exists.
I've written the following code:
template <typename>
struct sfinae_true : std::true_type{};
template <class F, typename T, typename... Us>
static auto test_templated_invoke_operator(int) ->
sfinae_true<decltype(std::declval<F>().template operator()<T>(std::forward(std::declval<Us>())... ))>;
template <class, typename, typename... Us>
static auto test_templated_invoke_operator(long) -> std::false_type;
template <class F, typename T, typename... Us>
struct has_templated_invoke_operator : decltype( test_templated_invoke_operator<F, T, Us...>(int{}) )
{ };
template <bool ActuallyInvoke, typename R, class F, typename T, typename... Ts>
struct invoke_if_possible_inner;
template <class F, typename R, typename T, typename... Ts>
struct invoke_if_possible_inner<false, R, F, T, Ts...>
{
R operator()(F, Ts&&...) {
return R();
}
};
template <class F, typename R, typename T, typename... Ts>
struct invoke_if_possible_inner<true, R, F, T, Ts...>
{
R operator()(F functor, Ts&&... params)
{
return functor.template operator()<T>(std::forward<Ts>(params)...);
}
};
template <typename T, typename R>
struct invoke_if_possible {
template <class F, typename... Ts>
R operator()(F functor, Ts&&... params)
{
constexpr bool actually_invoke = has_templated_invoke_operator<F, T, Ts...>::value;
// static_assert(actually_invoke == true,"Should be able to invoke for now!");
return invoke_if_possible_inner<actually_invoke, R, F, T, Ts...>{}(functor, std::forward<Ts>(params)...);
}
};
and here's a small main() function to test it with:
int main()
{
invoke_if_possible<int, double> iip;
auto result = iip(foo{}, 3.0);
std::cout << "Invoke if possible result is " << result << " (and should be 6.0)" << std::endl;
}
This fails (Coliru) - returning 0.0 instead of 6.0.
My question is: Why doesn't the code invoke the defined operator()? And how can I fix the detection mechanism so that its existence is acknowledged and it is invoked?
Notes:
The template parameter T is arbitrary; it has nothing to do with the Us parameters.
Yes, it's possible to derive R using the return type for operator() - but only if it exists. So we just provide it.
If you enable the static assert - it fails.

Your problem is here:
std::forward(std::declval<Us>())
std::forward takes a non-deduced template parameter which you're not providing - must be forward<T>(u) - so its deduction unconditionally fails.
But you don't even need the forward here at all. Just declval<Us>() suffices.

Related

Subset a Variadic template given a constexpr boolean selection function

Suppose we have a variadic templated class like
template<class...Ts>
class X{
template<size_t I>
constexpr bool shouldSelect();
std::tuple<TransformedTs...> mResults; // this is want I want eventually
};
where the implementation of shouldSelect is not provided, but what it does is that, given an index i referring to the ith element of the variadic Ts, tells you whether we should select it to the subset.
I want to do a transformation on Ts such that only classes Ts at indexes that results in shouldSelect returning true should be selected. Is there an easy way to do this?
For example, if shouldSelect returns true for I = 1,2,4, and Ts... = short, int, double, T1, T2, then I want to get a TransformedTs... that is made up of int, double, T2. Then I can use this TransformedTs... in the same class.
If you're able to use C++17, this is pretty easy to implement using a combination of if constexpr and expression folding.
Start with some helper types, one with parameters to track the arguments to X::shouldSelect<I>(), and the other with a type to test.
template <typename T, size_t I, typename...Ts>
struct Accumulator {
using Type = std::tuple<Ts...>;
};
template <typename T>
struct Next { };
Then an operator overload either adds the type to the accumulator, or not with if constexpr:
template <typename TAcc, size_t I, typename... Ts, typename TArg>
decltype(auto) operator +(Accumulator<TAcc, I, Ts...>, Next<TArg>) {
if constexpr (TAcc::template shouldSelect<I>()) {
return Accumulator<TAcc, I + 1, Ts..., TArg>{};
} else {
return Accumulator<TAcc, I + 1, Ts...>{};
}
}
Finally, you can put it all together with a fold expression and extract the type with decltype:
template <template <typename... Ts> class T, typename... Ts>
constexpr decltype(auto) FilterImpl(const T<Ts...>&) {
return (Accumulator<T<Ts...>, 0>{} + ... + Next<Ts>{});
}
template<typename T>
using FilterT = typename decltype(FilterImpl(std::declval<T>()))::Type;
Usage:
using Result = FilterT<X<int, double, bool, etc>>;
Demo: https://godbolt.org/z/9h89zG
If you don't have C++17 available to you, it's still possible. You can do the same sort of conditional type transfer using a recursive inheritance chain to iterate though each type in the parameter pack, and std::enable_if to do the conditional copy. Below is the same code, but working in C++11:
// Dummy type for copying parameter packs
template <typename... Ts>
struct Mule {};
/* Filter implementation */
template <typename T, typename Input, typename Output, size_t I, typename = void>
struct FilterImpl;
template <typename T, typename THead, typename... TTail, typename... OutputTs, size_t I>
struct FilterImpl<T, Mule<THead, TTail...>, Mule<OutputTs...>, I, typename std::enable_if<( T::template shouldSelect<I>() )>::type >
: FilterImpl<T, Mule<TTail...>, Mule<OutputTs..., THead>, (I + 1)>
{ };
template <typename T, typename THead, typename... TTail, typename... OutputTs, size_t I>
struct FilterImpl<T, Mule<THead, TTail...>, Mule<OutputTs...>, I, typename std::enable_if<( !T::template shouldSelect<I>() )>::type >
: FilterImpl<T, Mule<TTail...>, Mule<OutputTs...>, (I + 1)>
{ };
template <typename T, typename... OutputTs, size_t I>
struct FilterImpl<T, Mule<>, Mule<OutputTs...>, I>
{
using Type = std::tuple<OutputTs...>;
};
/* Helper types */
template <typename T>
struct Filter;
template <template <typename... Ts> class T, typename... Ts>
struct Filter<T<Ts...>> : FilterImpl<T<Ts...>, Mule<Ts...>, Mule<>, 0>
{ };
template <typename T>
using FilterT = typename Filter<T>::Type;
Demo: https://godbolt.org/z/esso4M

How to partially specialize a struct template for non-member & member function pointer

I'm doing some template metaprogramming involving function pointers. Because the function types for non-member and member function pointers are different, I'm trying to specialize between the two. Furthermore, I want the pointer to the function to be provided as a non-type template argument instead of a parameter to a function in the struct. So far here's what I have attempted:
template <typename T, T>
struct register_method;
template <typename R, typename... Args>
struct register_method<R(Args...), R (*method)(Args...)>
{
static R invoke(Args&&... params)
{
return (*method)(std::forward<Args>(params)...);
}
};
template <typename T, typename R, typename... Args>
struct register_method<R(Args...), R (T::*method)(Args...)>
{
static R invoke(T* instance, Args&&... params)
{
return (instance->*method)(std::forward<Args>(params)...);
}
};
However this fails to compile (here's just the first error):
prog.cc:14:48: error: 'Args' does not refer to a value
struct register_method<R(Args...), R (*method)(Args...)>
^
prog.cc:13:35: note: declared here
template <typename R, typename... Args>
^
I'm not really sure what it is trying to tell me. My goal is to utilize these objects like so:
void the_func(float val)
{
std::cout << "the_func called: " << val << "\n";
}
int main()
{
register_method<decltype(&the_func), &the_func>::invoke(50.f);
}
How can I get this working? If there's an opportunity to simplify, that would be great too (for example, if I can get away with just passing the function pointer to the template instead of also doing a decltype, which should reduce boilerplate.
EDIT: Also want to add that the reason I need separate specializations for non-member and member functions extend beyond just the function types being different. There is static state between the two that is different, due to the business rules I attach to the different function types. I have omitted those details here to keep the question simple.
Here's how you can fix the code:
template <typename T, T>
struct register_method;
template <typename R, typename... Args, R (*method)(Args...)>
struct register_method<R (*)(Args...), method>
{
template <typename ...P>
static R invoke(P &&... params)
{
return (*method)(std::forward<P>(params)...);
}
};
template <typename T, typename R, typename... Args, R (T::*method)(Args...)>
struct register_method<R (T::*)(Args...), method>
{
template <typename ...P>
static R invoke(T *instance, P &&... params)
{
return (instance->*method)(std::forward<P>(params)...);
}
};
Note that you have to introduce a separate parameter pack for the forwarding references to work, since they only work if the template parameter is being deduced.
And here is an alternative solution using C++17 auto template parameters:
template <auto method>
struct register_method;
template <typename R, typename... Args, R (*method)(Args...)>
struct register_method<method>
{
template <typename ...P>
static R invoke(P &&... params)
{
return (*method)(std::forward<P>(params)...);
}
};
template <typename T, typename R, typename... Args, R (T::*method)(Args...)>
struct register_method<method>
{
template <typename ...P>
static R invoke(T *instance, P &&... params)
{
return (instance->*method)(std::forward<P>(params)...);
}
};
Specialization should look like
template <typename T, T>
struct register_method;
template <typename R, typename... Args, R (*method)(Args...)>
struct register_method<R (*)(Args...), method>
{
static R invoke(Args... params)
{
return (*method)(std::forward<Args>(params)...);
}
};
template <typename C, typename R, typename... Args, C (T::*method)(Args...)>
struct register_method<R (C::*)(Args...), method>
{
static R invoke(T* instance, Args... params)
{
return (instance->*method)(std::forward<Args>(params)...);
}
};

Accessing the return type of a method

I'm having a hard time getting this simple thing going.
One thing I found that works:
#include <type_traits>
struct A
{
int Method();
};
static_assert( std::is_same_v<
decltype(A{}.Method()), int
>
); // pass. cool.
Ok great. But no; not great. Because I now have a default constructible requirement, AND I need to write a call expression with all arguments. And who knows about them !
Consider the real situation:
struct A
{
int Method(MysteriousArgumentsIDontCareAboutAndCanChangeInTheFuture);
};
static_assert( std::is_same_v<
decltype(A{}.Method()), int
>
); // not so cool anymore (too few arguments to function call, expected 1, have 0)
How about using std::invoke_result ?
static_assert( std::is_same_v<
std::invoke_result_t< A::Method >, int
>
);
nah.
call to non-static member function without an object argument
MSVC says
non-standard syntax; use '&' to create a pointer to member
I can fiddle all I want with this expression, nothing good comes out of it.
e.g.:
using T = std::invoke_result_t< decltype(&A::Method) >;
error: no type named 'type' in 'std::invoke_result
If I remove the decltype it's type-value mismatch (of course) etc...
cppreference.com mentions this usage for the C++14 version:
std::result_of<decltype(&C::Func)(C, char, int&)>::type
Not much better than my first attempt. All the arguments are still there.
In action in our simple case: https://godbolt.org/z/KtQbth
Help ?
You can use the trait suggested by Piotr Skotnicki:
template <typename T>
struct return_type;
template <typename R, typename... Args>
struct return_type<R(Args...)> { using type = R; };
template <typename R, typename... Args>
struct return_type<R(*)(Args...)> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...)> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) &> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) &&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) const> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) const&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) const&&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) volatile> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) volatile&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) volatile&&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) const volatile> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) const volatile&> { using type = R; };
template <typename R, typename C, typename... Args>
struct return_type<R(C::*)(Args...) const volatile&&> { using type = R; };
template <typename T>
using return_type_t = typename return_type<T>::type;
Now you can do:
static_assert(std::is_same_v<return_type_t<decltype(&A::Method)>, int>);
[static_assert( std::is_same_v< decltype(std::declval<A>().Method()), int >);//super cool now][1]

SFINAE for function object with default argument

What is the simplest way to tell at compile time if a lambda (or a function object) has a default argument or not?
Example:
auto f = [](int i=0){};
auto g = [](int i){};
static_assert(has_default_arg<decltype(f)>::value==true);
static_assert(has_default_arg<decltype(g)>::value==false);
I do not believe it is possible to detect this without some sort of function static reflection. You could however check if the lambda is invocable with both zero and one arguments. Example using the detection idiom:
template <class T>
using invocable_zero = decltype(std::declval<T&>()());
template <class T, class X>
using invocable_one = decltype(std::declval<T&>()(std::declval<X>()));
template <class T, class X>
struct has_default_arg : std::conjunction<
std::experimental::is_detected<invocable_zero, T>,
std::experimental::is_detected<invocable_one, T, X>
> { };
live example on wandbox.org
As NathanOliver pointed out in the comment, you cannot do this for normal function object. So we only focus on lambda.
First, we can make a helper class that checks whether F can be invoked with the arguments chosen from Args... via an index sequence Index_sequence:
template <typename F, typename Index_sequence, typename... Args>
struct is_invocable_for_indices : std::false_type {};
template <typename F, size_t... Is, typename... Args>
struct is_invocable_for_indices<F, std::index_sequence<Is...>, Args...>
: std::is_invocable<F, std::tuple_element_t<Is, std::tuple<Args...>>...> {};
template <typename F, typename Index_sequence, typename... Args>
inline constexpr bool is_invocable_for_indices_v = is_invocable_for_indices<F, Index_sequence, Args...>::value;
// example use
auto f = [](int i = 0) {};
auto g = [](int i) {};
static_assert(is_invocable_for_indices_v<decltype(f), std::index_sequence<>, int>);
static_assert(!is_invocable_for_indices_v<decltype(g), std::index_sequence<>, int>);
static_assert(is_invocable_for_indices_v<decltype(g), std::index_sequence<0>, int>);
Let Args be the parameter types of F, which can be detected via decltype(&F::operator()) (the idea comes from this answer). Now you can check if F has default argument by checking if F can be invoked with the first sizeof...(Args) - 1 arguments of Args. So we can define has_defulat_arg as follows:
template <typename F, typename OperatorType>
struct has_defulat_arg_impl : std::false_type {};
template <typename F, typename R, typename... Args>
struct has_defulat_arg_impl<F, R(F::*)(Args...) const>
: is_invocable_for_indices<F, std::make_index_sequence<sizeof...(Args) - 1>, Args...> {};
// specialization for the case where sizeof...(Args) == 0
template <typename F, typename R>
struct has_defulat_arg_impl<F, R(F::*)() const> : std::false_type {};
template <typename F>
using has_defulat_arg = has_defulat_arg_impl<F, decltype(&F::operator())>;
template <typename F>
inline constexpr bool has_defulat_arg_v = has_defulat_arg<F>::value;
// example use
auto f = [](int i = 0) {};
auto g = [](int i) {};
static_assert(has_defulat_arg_v<decltype(f)>);
static_assert(!has_defulat_arg_v<decltype(g)>);
LIVE EXAMPLE

Counting arguments of an arbitrary callable with the C++ detection idiom

I've been using the C++ detection idiom create a metafunction for determining the number of arguments to an arbitrary callable. So far, I have this (full, compilable code at http://ideone.com/BcgDhv):
static constexpr auto max_num_args = 127;
struct any { template <typename T> operator T() { } };
template <typename F, typename... Args>
using callable_archetype = decltype( declval<F>()(declval<Args>()...) );
template <typename F, typename... Args>
using is_callable_with_args = is_detected<callable_archetype, F, Args...>;
template <typename F, size_t I = 0, typename... Args>
struct count_args
: conditional<is_callable_with_args<F, Args...>::value,
integral_constant<size_t, I>,
count_args<F, I+1, Args..., any>
>::type::type
{ };
template <typename F, typename... Args>
struct count_args<F, max_num_args, Args...> : integral_constant<size_t, max_num_args> { };
This works great when none of the callable arguments are lvalue references:
void foo(int i, int j) { }
static_assert(count_args<decltype(foo)>::value == 2, "");
But when any of the arguments are lvalue references, this fails (for obvious reasons, since the callable archetype has a substitution failure):
void bar(char i, bool j, double& k);
static_assert(count_args<decltype(bar)>::value == 3, "doesn't work");
Does anyone know how to generalize this idea to make it work with lvalue references as well?
The following works (for small max_num_args):
struct any { template <typename T> operator T(); };
struct anyref { template <typename T> operator T&(); };
template <typename F, typename... Args>
using callable_archetype = decltype(std::declval<F>()(std::declval<Args>()...) );
template <typename F, typename... Args>
using is_callable_with_args = std::is_detected<callable_archetype, F, Args...>;
template <typename F, size_t I = 0, typename... Args>
struct count_args
: std::conditional<is_callable_with_args<F, Args...>::value,
std::integral_constant<std::size_t, I>,
std::integral_constant<std::size_t,
std::min(count_args<F, I+1, Args..., any>::value,
count_args<F, I+1, Args..., anyref>::value)>
>::type::type
{};
template <typename F, typename... Args>
struct count_args<F, max_num_args, Args...> :
std::integral_constant<std::size_t, max_num_args> {};
Demo
But code has to be optimized, as the complexity is 2**max_num_args :/
Change this line:
struct any { template <typename T> operator T() { } };
to:
struct any {
template <typename T> operator T&&() { }
template <typename T> operator T&() { }
};
live example
We have both an lvalue and rvalue implicit casting operator. So, we ... good?
Building off of the answer from #Jarod42, a slightly better definition of any seems to do the trick in the vast majority of cases (excluding cases that cause callable_archetype to be a substitution error for other reasons; for instance, classes with deleted copy constructors, the invocation of which wouldn't be valid anyway):
struct any {
template <typename T,
typename = enable_if_t<
not is_same<T, remove_reference_t<T>>::value
>
>
operator T();
template <typename T,
typename = enable_if_t<
is_same<T, remove_reference_t<T>>::value
>
>
operator T&();
template <typename T,
typename = enable_if_t<
is_same<T, remove_reference_t<T>>::value
>
>
operator T&&();
};
This seems to work in all of the same cases as the previous answer without the exponential scaling.
Demo