Decomposing C++17 function-like object in generic fashion? - c++

In C++17 suppose I have a function-like object passed as a parameter to some template:
template<typename F>
void g(F f) {
auto x = f(/*...*/);
}
There are lots of different types F could be, such as a function pointer, a std::function, a lambda expression, and in fact any class type that implements operator().
Is there any way to get the function-like objects arity and type of its parameters and the type of its return type?
I mean, ultimately F could be a class that overloads operator() with multiple different member functions, each with different arities, parameter types and return types - so there isn't a fully-general answer (unless there is some way to iterate that overload set, which I don't think there is).
But for the typical case where a function call expression involving f results in a single overload, is there a solution?
(also if there is any progress in C++20, worth mentioning too)

C++17's adds deduction guides for std::function, which we can use to do the deduce the function signature of non-overloaded function-like objects:
template <typename R, typename... Args>
constexpr auto do_something_with_the_signature(std::function<R(Args...)>) {
// Assuming that you only care about the return type.
// Otherwise, you may want some kind of wrapper to extract the signature to
// avoid runtime cost
}
...
using some_type_computation =
decltype(do_something_with_the_signature(std::function(f)));
If you only wanted the return type, you could just use:
using result_type = typename decltype(std::function(f))::result_type;
If you want to avoid std::function altogether because of the compile-time costs, you can implement your own version of the deduction guides for your own type (possibly as general as a function_traits type trait). A sketch of how you might implement the deduction guides yourself can be seen in my answer here: https://stackoverflow.com/a/66038056/1896169

Related

Is it possible to determine if a callable is a predicate (i.e. returns bool)?

In an attempt to rewrite a predicate combinator like this
auto constexpr all = [](auto const&... predicates){
return [predicates...](auto const&... x){
return (predicates(x...) && ...);
};
};
(little generalization of this) in a way that it would give meaningful errors when fed with non-predicates/predicates with different arities/arguments, I started writing something like this:
template<typename T, typename = void>
struct IsPredicate : public std::false_type {};
template<typename T>
struct IsPredicate<T, std::enable_if_t<std::is_same_v<bool, return_type_of_callable_T>, void>>
: public std::true_type {};
and then I stared at it for a while... How do I even check what is the return type of a function, if I don't even know how to call it?
I see this:
I couldn't even pass decltype(overloaded_predicate_function) to IsPredicate, because template type deduction can't occur with an overloaded name,
even if I only talk of function objects, the problem of the first bullet point could apply to operator(), in case it is overloaded.
So my question is: is it even possible to determine the return type of an arbitrary callable?
I'm mostly interested in a C++17 answer, but, why not?, I'd also like to know what C++20's concept offer in this respect.
So my question is: is it even possible to determine the return type of an arbitrary callable?
No. You can only do this in very narrow circumstances:
the callable is a pointer to member data / pointer to member function
the callable is a pointer/reference to function
the callable is a function object with a single non-overloaded function call operator that is not a template and no conversion functions to function pointers/reference
That's it. If you have a function object whose call operator is either overloaded or a template, you can't really figure out what its return type is. Its return type could depend on its parameter type, and you may not have a way of knowing what the parameter types could be. Maybe it's a call operator template that only accepts a few specific types that you have no way of knowing about, but it is a predicate for those types?
The best you can do is defer checking until you know what what arguments are. And then C++20 already has the concept for you (predicate):
inline constexpr auto all = []<typename... Ps>(Ps const&... predicates){
return [=]<typename... Xs>(Xs const&... x)
requires (std::predicate<Ps const&, Xs const&...> && ...)
{
return (std::invoke(predicates, x...) && ...);
};
};
Note that you should use std::invoke to allow for pointers to members as predicates as well (and this is what std::predicate checks for).
You cannot determine the return type of a callable without specifying the argument types (usually done by providing actual arguments) because the return type could depend on the argument types. "decltype(potential_predicate)" won't work but "decltype(potential_predicate(args...))" is another matter.
The first will be the type of the callable itself (whether pointer-to-function or a class type or whatever) while the second will produce the return type of the callable expression.
Can't give you a C++17 answer, but since you also asked for concepts:
The requires expression states that the () operator is overloaded and returns a bool. I think a predicate in the classical sense takes two arguments, but the concept can be easily extended to fo fulfill that requirement as well.
template<typename T>
concept IsPredicate =
requires(T a) {
{ a() } -> std::same_as<bool>;
};

How to pass real type of auto into template

So I have something for example
auto a = getMyTuple();
which will eventually be real type of std::tuple<(some args)>
Now I want to store this in a class which has a template since I dont know the type yet. Something along the lines of
template<typename T>
class my_tuple
{
public:
T mt;
my_tuple(T t)
{
mt = t;
}
};
My question is, is there a way to get the type returned by auto, so I can pass it into the template class like
my_tuple<getType(a)> myTup(a);
That's what decltype is for:
my_tuple<decltype(a)> myTup(a);
You want decltype (since C++11):
my_tuple<decltype(a)> myTup(a);
You could implement a factory function that will construct your objects, the same way as std::make_tuple() constructs std::tuple.
(simplified version, see more plausible version in the link above or in your favorite standard library's source code)
template <typename T>
my_tuple<T> my_make_tuple(T t) {
return my_tuple<T>(t);
}
auto myTup = my_make_tuple(a);
Function call template argument deduction will figure out the types automagically, so you don't need to worry about explicit types anymore.
This is how they do it in standard library.
See this talk by Stephan T. Lavavej: Don’t Help the Compiler (towards the second half of it)
Update:
I think that the solutions with decltype posted around are ugly and error prone, because of the repetition of the variable name (violation of DRY principle). Also, decltype is unnecessary here as types can be deduced automatically with the use of a function wrapper. Finally, if your tuple would have 25 parameters will you write decltype 25 times? What if you accidentally mix the order of types and the order of parameters?

Why does Boost MPL have integral constants?

Since you can take integral values as template parameters and perform arithmetic on them, what's the motivation behind boost::mpl::int_<> and other integral constants? Does this motivation still apply in C++11?
You can take integral values as template parameters, but you cannot take both types and non-type template parameters with a single template. Long story short, treating non-type template parameters as types allows for them to be used with a myriad of things within MPL.
For instance, consider a metafunction find that works with types and looks for an equal type within a sequence. If you wished to use it with non-type template parameters you would need to reimplement new algorithms 'overloads', a find_c for which you have to manually specify the type of the integral value. Now imagine you want it to work with mixed integral types as the rest of the language does, or that you want to mix types and non-types, you get an explosion of 'overloads' that also happen to be harder to use as you have to specify the type of each non-type parameter everywhere.
This motivation does still apply in C++11.
This motivation will still apply to C++y and any other version, unless we have some new rule that allows conversion from non-type template parameters to type template parameters. For instance, whenever you use 5 and the template requests a type instantiate it with std::integral_constant< int, 5 > instead.
tldr; Encoding a value as a type allows it to be used in far more places than a simple value. You can overload on types, you can't overload on values.
K-Ballo's answer is great.
There's something else I think is relevant though. The integral constant types aren't only useful as template parameters, they can be useful as function arguments and function return types (using the C++11 types in my examples, but the same argument applies to the Boost ones that predate them):
template<typename R, typename... Args>
std::integral_constant<std::size_t, sizeof...(Args)>
arity(R (*)(Args...))
{ return {}; }
This function takes a function pointer and returns a type telling you the number of arguments the function takes. Before we had constexpr functions there was no way to call a function in a constant expression, so to ask questions like "how many arguments does this function type take?" you'd need to return a type, and extract the integer value from it.
Even with constexpr in the language (which means the function above could just return sizeof...(Args); and that integer value would be usable at compile time) there are still good uses for integral constant types, e.g. tag dispatching:
template<typename T>
void frobnicate(T&& t)
{
frob_impl(std::forward<T>(t), std::is_copy_constructible<T>{});
}
This frob_impl function can be overloaded based on the integer_constant<bool, b> type passed as its second argument:
template<typename T>
void frob_impl(T&& t, std::true_type)
{
// do something
}
template<typename T>
void frob_impl(T&& t, std::false_type)
{
// do something else
}
You could try doing something similar by making the boolean a template parameter:
frob_impl<std::is_copy_constructible<T>::value>(std::forward<T>(t));
but it's not possible to partially specialize a function template, so you couldn't make frob_impl<true, T> and frob_impl<false, T> do different things. Overloading on the type of the boolean constant allows you to easily do different things based on the value of the "is copy constructible" trait, and that is still very useful in C++11.
Another place where the constants are useful is for implementing traits using SFINAE. In C++03 the conventional approach was to have overloaded functions that return two types with different sizes (e.g an int and a struct containing two ints) and test the "value" with sizeof. In C++11 the functions can return true_type and false_type which is far more expressive, e.g. a trait that tests "does this type have a member called foo?" can make the function indicating a positive result return true_type and make the function indicating a negative result return false_type, what could be more clear than that?
As a standard library implementor I make very frequent use of true_type and false_type, because a lot of compile-time "questions" have true/false answers, but when I want to test something that can have more than two different results I will use other specializations of integral_constant.

why do lambda functions in c++11 not have function<> types?

I am playing around with the c++11 functional features. One thing I find odd is that the type of a lambda function is actually NOT a function<> type. What's more, lambda's do not seem to play really well with the type-inferencing mechanism.
Attached is a small example in which I tested flipping the two arguments of a function for adding two integers. (The compiler I used was gcc 4.6.2 under MinGW.) In the example, the type for addInt_f has been explicitly defined using function<> while addInt_l is a lambda whose type is type-inferenced with auto.
When I compiled the code, the flip function can accept the explicitly type-defined version of addInt but not the lambda version, giving an error saying that,
testCppBind.cpp:15:27: error: no matching function for call to 'flip(<lambda(int, int)>&)'
The next few lines show that the lambda version (as well as a 'raw' version) can be accepted if it's explicitly cast to the appropriate function<> type.
So my questions are:
Why is it that a lambda function does not have a function<> type in the first place? In the small example, why does not addInt_l have function<int (int,int)> as the type instead of having a different, lambda type? From the perspective of functional programming, what's the difference between a function/functional object and a lambda?
If there is a fundamental reason that these two have to be different. I heard that lambda's can be converted to function<> but they are different. Is this a design issue/defect of C++11, an implementation issue or is there a benefit in distinguishing the two as the way it is? It seems that the type-signature of addInt_l alone has provided enough information about the parameter and return types of the function.
Is there a way to write the lambda so that the above mentioned explicit type-casting can be avoided?
Thanks in advance.
//-- testCppBind.cpp --
#include <functional>
using namespace std;
using namespace std::placeholders;
template <typename T1,typename T2, typename T3>
function<T3 (T2, T1)> flip(function<T3 (T1, T2)> f) { return bind(f,_2,_1);}
function<int (int,int)> addInt_f = [](int a,int b) -> int { return a + b;};
auto addInt_l = [](int a,int b) -> int { return a + b;};
int addInt0(int a, int b) { return a+b;}
int main() {
auto ff = flip(addInt_f); //ok
auto ff1 = flip(addInt_l); //not ok
auto ff2 = flip((function<int (int,int)>)addInt_l); //ok
auto ff3 = flip((function<int (int,int)>)addInt0); //ok
return 0;
}
std::function is a tool useful to store any kind of callable object regardless of its type. In order to do this it needs to employ some type erasure technique, and that involves some overhead.
Any callable can be implicitly converted to a std::function, and that's why it usually works seamlessly.
I'll repeat to make sure it becomes clear: std::function is not something just for lambdas or function pointers: it's for any kind of callable. That includes things like struct some_callable { void operator()() {} };, for example. That is a simple one, but it could be something like this instead:
struct some_polymorphic_callable {
template <typename T>
void operator()(T);
};
A lambda is just yet another callable object, similar to instances of the some_callable object above. It can be stored in a std::function because it's callable, but it doesn't have the type erasure overhead of std::function.
And the committee plans to make lambdas polymorphic in the future, i.e., lambdas that look like some_polymorphic_callable above. Which std::function type would such a lambda be?
Now... Template parameter deduction, or implicit conversions. Pick one. That's a rule of C++ templates.
To pass a lambda as a std::function argument, it needs to be implicitly converted. Taking a std::function argument means that you're choosing implicit conversions over type deduction. But your function template needs the signature to be deduced or provided explicitly.
The solution? Don't restrict your callers to std::function. Accept any kind of callable.
template <typename Fun>
auto flip(Fun&& f) -> decltype(std::bind(std::forward<Fun>(f),_2,_1))
{ return std::bind(std::forward<Fun>(f),_2,_1); }
You may now be thinking why do we need std::function then. std::function provides type erasure for callables with a known signature. That essentially makes it useful to store type-erased callables and to write virtual interfaces.
Because function<> employs type erasure. This allows several different function-like types to be stored in a function<>, but incurs a small runtime penalty. Type erasure hides the actual type (your specific lambda) behind a virtual function interface.
There is a benefit to this: one of the C++ design "axioms" is to never add overhead unless it is really needed. Using this setup, you do not have any overhead when using type inference (use auto or pass as a template parameter), but you still have all the flexibility to interface with non-template code through function<>. Also note that function<> is not a language construct, but a component of the standard library that can be implemented using simple language features.
No, but you can write the function to just take the type of the function (language construct) instead of the specifics of the function<> (library construct). Of course, that makes it a lot harder to actually write down the return type, since it does not directly give you the parameter types. However, using some meta-programming a la Boost.FunctionTypes you can deduce these from the function you pass in. There are some cases where this is not possible though, for example with functors that have a templated operator().

Difference between std::result_of and decltype

I have some trouble understanding the need for std::result_of in C++0x. If I understood correctly, result_of is used to obtain the resulting type of invoking a function object with certain types of parameters. For example:
template <typename F, typename Arg>
typename std::result_of<F(Arg)>::type
invoke(F f, Arg a)
{
return f(a);
}
I don't really see the difference with the following code:
template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(f(a)) //uses the f parameter
{
return f(a);
}
or
template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(F()(a)); //"constructs" an F
{
return f(a);
}
The only problem I can see with these two solutions is that we need to either:
have an instance of the functor to use it in the expression passed to decltype.
know a defined constructor for the functor.
Am I right in thinking that the only difference between decltype and result_of is that the first one needs an expression whereas the second does not?
result_of was introduced in Boost, and then included in TR1, and finally in C++0x. Therefore result_of has an advantage that is backward-compatible (with a suitable library).
decltype is an entirely new thing in C++0x, does not restrict only to return type of a function, and is a language feature.
Anyway, on gcc 4.5, result_of is implemented in terms of decltype:
template<typename _Signature>
class result_of;
template<typename _Functor, typename... _ArgTypes>
struct result_of<_Functor(_ArgTypes...)>
{
typedef
decltype( std::declval<_Functor>()(std::declval<_ArgTypes>()...) )
type;
};
If you need the type of something that isn't something like a function call, std::result_of just doesn't apply. decltype() can give you the type of any expression.
If we restrict ourselves to just the different ways of determining the return type of a function call (between std::result_of_t<F(Args...)> and decltype(std::declval<F>()(std::declval<Args>()...)), then there is a difference.
std::result_of<F(Args...) is defined as:
If the expression
INVOKE (declval<Fn>(), declval<ArgTypes>()...) is well
formed when treated as an
unevaluated operand (Clause 5), the
member typedef type shall name the
type decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...));
otherwise, there shall be no member
type.
The difference between result_of<F(Args..)>::type and decltype(std::declval<F>()(std::declval<Args>()...) is all about that INVOKE. Using declval/decltype directly, in addition to being quite a bit longer to type, is only valid if F is directly callable (a function object type or a function or a function pointer). result_of additionally supports pointers to members functions and pointers to member data.
Initially, using declval/decltype guaranteed a SFINAE-friendly expression, whereas std::result_of could give you a hard error instead of a deduction failure. That has been corrected in C++14: std::result_of is now required to be SFINAE-friendly (thanks to this paper).
So on a conforming C++14 compiler, std::result_of_t<F(Args...)> is strictly superior. It's clearer, shorter, and correctly† supports more Fs‡ .
†Unless, that is, you're using it in a context where you don't want to allow pointers to members, so std::result_of_t would succeed in a case where you might want it to fail.
‡ With exceptions. While it supports pointers to members, result_of will not work if you try to instantiate an invalid type-id. These would include a function returning a function or taking abstract types by value. Ex.:
template <class F, class R = result_of_t<F()>>
R call(F& f) { return f(); }
int answer() { return 42; }
call(answer); // nope
The correct usage would've been result_of_t<F&()>, but that's a detail you don't have to remember with decltype.