I would like to make a universal wrapper function which can accept any
free standing or static function,
member function ( perhaps as a specialization with the first argument used as *this)
including overloaded or templated cases together with variable arguments. Such wrapper will then, in the body, call the function exactly with the forwarded parameters.
Example:
template<typename Fnc,typename...Args>
void wrapper(Fnc fnc, Args&&...args){
// Do some stuff
// Please disregard the case when return type is void,
// that be SFINAED with std::result_of.
auto res = fnc(std::forward<Args>(args)...);
// Do some stuff
return res;
}
#include <vector>
auto foo(int i){ return i; }
auto foo(double& d){ return d; }
auto foo(double&& d){ return d; }
auto foo(const char* str){ return str; }
template<typename...T>
auto generic_foo(T&&...ts){/* ...*/ return /* ...*/; }
template<typename T>
void constrained_foo(std::vector<T>& lref,std::vector<T>&& rref, std::vector<T> value){ /**/}
int main(){
// Basics
wrapper(foo, 1);// foo(int)
wrapper(foo, 1.1); // foo(double&&)
wrapper(foo, "ahoj"); // foo(const char*)
// Conversion must work too
wrapper(foo, 1.1f); // foo(double&&)
wrapper(foo, (short)1); // foo(int)
// Detecting lvalues, rvalues is also a must
double y;
foo(y);
foo(std::move(y));
// Similarly for templates
int x;
std::vector<int> v1, v2, v3;
wrapper(generic_foo, 1, 1.1, "ahoj", x, &x);
wrapper(constrained_foo, v1, std::move(v2), v3);
}
The thing I do find so frustrating about this is that I am supplying all the necessary information to make these calls right next to each other, there is no added ambiguity about what to call, I could call it myself, I could(will) make a macro that can do it, but there is no proper C++ syntax for it AFAIK.
I discovered the need for it while trying to automatically call all my methods in certain "context". But thinking about it more, I believe this could have really widespread usage for <algorithm>, <thread> libraries too. Where you would not have to make one-statement lambdas just to call a function/operator with the lambdas parameters or something captured.
The issue arises in any function accepting another function which will be eventually called together with the passed known parameters.
My attempts
generic_foo can be resolved if the return type is fixed:
template<typename...Args>
void wrapper(void(*f)(Args&&... args) , Args&&...args){
// ...
f(std::forward<Args>(args)...);
}
int main(){
int x;
wrapper(generic_foo, 1, 1.1, "ahoj", x, &x, std::move(x));
}
This works nicely, the return type can maybe be resolved too by some obscure and clever use of std::invoke_result_t, but currently it is a kind of chicken-egg situation with the parameter type. Because the only thing how to resolve the name generic_foo is to force it to decay to a function pointer and then there is no name to put in std::invoke_result_t since the parameter is still being deduced.
This will also work with overloads as long as there is an exact match, so it cannot do conversions.
This approach is as far as I can get without macros when the function name is not known in advance.
If the name of the callable is fixed, there is this frequently-used variation of lambda trick:
template<typename Fnc, typename...Args>
void wrapper(Fnc f , Args&&...args){
// ...
f(std::forward<Args>(args)...);
}
int main(){
int x;
wrapper([](auto&&...args)
{ return generic_foo(std::forward<decltype(args)>(args)...);},
1, 1.1, "ahoj", x, &x, std::move(x));
}
If I add a macro doing exactly this:
#define WRAP_CALL(wrapper_fnc, called_fnc, ...) \
wrapper_fnc([&](auto&&...args) \
{ return called_fnc(std::forward<decltype(args)>(args)...);}, \
__VA_ARGS__ );
int main(){
int x;
WRAP_CALL(wrapper, generic_foo, 1, 1.1, "ahoj", x, &x, std::move(x))
}
I get the least macro-infested working solution I can think of, it works with any callable and any wrappers which can stay proper C++ functions. But I would like macro-less version of this and especially for functions.
So I will probably still use this version, does not seem too unreasonable. Are there any corners cases I should know about?
I did not write a single C++20 concept yet, so I still have very small hope there might be something that can work in that area perhaps? But probably not since std::thread(foo,1); also suffers from it.
So this might sadly require language changes because the name of the overload set or a template cannot currently be passed anywhere, even just as some kind of aggregated type. So perhaps something akin to std::initializer_list class plus its sometimes-magical syntax?
If this is indeed the case, I would gladly accept any answer listing any currently active proposals that might help with this. If there are even any.
I did found N3579 - A Type trait for signatures which could perhaps work together with the function pointer solution if the chicken-egg problem is addressed. But the proposal looks very dead.
The "overloaded or templated cases" are not entities that can be function/template arguments—certain cases where overload resolution can use contextual information notwithstanding. What you mean by foo in your wrapper(foo,…) is little more than a token (in particular, it's an overload set), and the language simply has no way of addressing such an object since it has no ("aggregated"?) type. (Conversely, macros do operate on tokens, which is why they are applicable here.) You seem to know most of this, but it may be helpful to see why it's knowably impossible, and why it's a bad idea to think of an overload set as "a callable". (After all, syntactically a type name is also "callable", but it wouldn't make any sense to pass one as a function argument.)
Even if a call f(g,…) were defined to try each overload of g in turn (which it is, for the narrow purpose of deducing f's template arguments), that wouldn't help for (an overload set containing) a template: there would be no way to even evaluate f's SFINAE conditions given a g template for which a specialization had not yet been chosen.
The standard lambda trick, which you also illustrated, is a way of performing overload resolution with the benefit of the argument list, which is why it's pretty much the only approach that works. There are certainly proposals to automate that process, including the vagaries of SFINAE-friendliness and exception specifications.
Consider this instead:
template <class T>
auto wrapper(T fnc) {
// Do some stuff
auto res = fnc(); // <--- no arguments
// Do more stuff
return res;
}
Since arguments are known at wrapper call time, they can also be bound at wrapper call time.
wrapper([](){ return foo(1); });
wrapper([](){ return foo(1.1); });
wrapper([](){ return foo("ahoj"); });
wrapper([&x](){ return generic_foo(1, 1.1, "ahoj", x, &x); });
You can encapsulate this in a macro, which is of course less than ideal, but at least this one is short and readable.
#define DEFER(x) ([&](){return x;})
wrapper(DEFER(foo(1)));
wrapper(DEFER(foo(1.1)));
wrapper(DEFER(foo("ahoj")));
wrapper(DEFER(generic_foo(1, 1.1, "ahoj", x, &x)));
This doesn't do exactly what you want, for example, in
wrapper(DEFER(foo(bar())))
bar is called late. This is fixable with a bit of syntax:
wrapper([z=bar()](){ return foo(z); });
and of course this can be wrapped in a macro too:
wrapper(DEFER1(foo(z), z=bar()));
although this is getting a bit unwieldy.
Related
I have code that clips some value to be between a range centered around 0 like below.
Eigen::VectorXd a;
Eigen::VecotrXd b;
a = a.cwiseMin(b).cwiseMax(-b); // no temporary created here?
I want to factor out the logic into a function.
One solution:
Eigen::VectorXd Clip(const Eigen::VectorXd& a, const Eigen::VectorXd& b);
a = Clip(a, b);
But I assume this is inefficient as it creates an extra temporary?
Another solution:
void Clip(Eigen::Ref<Eigen::VectorXd> a, const Eigen::VectorXd& b) {
a = a.cwiseMin(b).cwiseMax(-b);
}
But this seems inconvenient to use sometimes:
void SomeFunctionSignatureICannotChange(const Eigen::VectorXd& a, const Eigen::VectorXd& b) {
// Eigen::VectorXd a_clipped = Clip(a, b); would be cleaner.
Eigen::VectorXd a_clipped;
Clip(a_clipped, b);
}
The best solution I can think of:
template <typename DerivedV, typename DerivedB>
auto Clip(const Eigen::ArrayBase<DerivedV>& v,
const Eigen::ArrayBase<DerivedB>& bound)
-> decltype(v.min(bound).max(-bound)) {
return v.min(bound).max(-bound);
}
(I assume 'auto' in this case is fine and not the one that common pitfalls warned against?)
However, the code seems template-heavy and a bit-complicated. For example, trailing return type is discouraged by google style guide here:
Use the new trailing-return-type form only in cases where it's
required (such as lambdas) or where, by putting the type after the
function's parameter list, it allows you to write the type in a much
more readable way. The latter case should be rare; it's mostly an
issue in fairly complicated template code, which is discouraged in
most cases.
Alternatively, if I remove the trailing return type, function return type deduction will kick in. But google style guide seems to also discourage function return type deduction in public headers here
Furthermore, use it only if the function or lambda has a very narrow
scope, because functions with deduced return types don't define
abstraction boundaries: the implementation is the interface. In
particular, public functions in header files should almost never have
deduced return types.
I'm new to Eigen and C++ so not sure if I missed anything. Hope to learn from everyone's comments and suggestions. Thanks!
I confirm that a = a.cwiseMin(b).cwiseMax(-b); does not create any temporary. In your case, using auto return type is highly recommended, and I would also argue that your use case is a perfectly legit exception to the aforementioned rules:
Writing the return type explicitly would be a nightmare and error prone.
This is a very short function that is expected to be inlined. It is thus expected to be declared and defined at the same time. Therefore the second rule does not not really apply.
In c++14 you can even omit the redundancy:
template <typename DerivedV, typename DerivedB>
auto Clip(const Eigen::ArrayBase<DerivedV>& v,
const Eigen::ArrayBase<DerivedB>& bound)
{
return v.min(bound).max(-bound);
}
The auto keyword can be used in many contexts and combined with other language elements, as in const auto& or auto&&. However, it cannot be used in cast operations. Consider the following example (which does not compile):
template <class T> void foo(T&& t) {}
int main() {
int i = 0;
foo(static_cast<const auto&>(i)); // This could call foo(const int&)
}
The cast would obviously work with int instead of auto, but being able to use auto in casts would be useful in generic code, for instance:
You could use const auto& (as above) to explicitly call an overloaded function with a const lvalue ref.
Similarly, you could use static_cast<auto&&> as a less verbose version of std::forward<decltype(x)>(x).
Thinking a bit further, static_cast<auto>(x) or something like auto(x) could be a way of explicitly copying an object.
(Note that explicit types instead of auto can be used in all these scenarios.)
To be honest, I intuitively expected this syntax to work (because it mirrors the way auto can be used in declarations), but to my mild surprise it didn't. Since it is such an obvious syntax, I am wondering whether there are specific reasons why it is disallowed? Or is it simply a case of nobody considering/proposing it as a feature?
Let's say I'm implementing a function func.
I have a use case where func will be invoked with two parameters.
I have another use case where func will be invoked with just one parameter but a default value of the missing second parameter will be used within the function.
I can think of 3 possible ways of implementing this functionality:
Default Argument
void func(int a, long b = c);
func will be invoked like this:
func(a);
func(a, b);
Function Overloading
void func(int a);
void func(int a, long b);
func will be invoked like this:
func(a);
func(a, b);
Using Optional as a function argument
void func(int a, optional<long> b);
func will be invoked like this:
func(a, optional<long> ());
func(a, b);
I want to know what's the best way of implementing the desired functionality. Any suggestions are welcome.
There are two questions to ask yourself when considering this:
Does the optional argument have a logical default?
Is the function implemented the same when the optional argument is given and when it's not?
If the second argument has a logical default and the function does the same thing no matter what, then a default argument works well. For example:
std::vector<std::string> split_string(const std::string& str, char sep = ' ');
By default, this splits a string on spaces, but the separator can be given to change that. It makes little sense to split this into two overloads or use a std::optional here.
If the second argument doesn't have a logical default but the function is still mostly the same if it isn't given, then std::optional makes more sense. For example:
void extract(const std::filesystem::path& archive_file,
const std::filesystem::path& output_dir,
std::optional<std::regex> exclude_filter = {});
Here we're extracting files from an archive file and writing the extracted files to disk, optionally excluding files that match some pattern. The function definition would be fundamentally the same with or without the filter; it's just one extra line difference:
if (exclude_filter && std::regex_match(file, *exclude_filter) continue;
It doesn't make much sense to duplicate the definition, so overloading doesn't really make sense. At the same time, there's no "don't match anything" regex, so there's not a logical default filter that could be applied. std::optional is a perfect fit here.
Note that I did still use a default argument. Defaulting your std::optional to be empty can make your calls much nicer.
Lastly, if the function implementation is fundamentally different between the one-arg and two-arg versions, then use an overload. For example:
void hide(window& win) {
win.set_visible(false);
}
template <typename rep, typename period>
void hide(window& win, const std::chrono::duration<rep, period>& fade_time) {
auto wait_time = equal_intervals(fade_time, win.opacity());
while (win.oapcity() > 0) {
win.set_opacity(win.opacity() - 1);
std::this_thread::sleep_for(wait_time);
}
}
Here we're hiding a window with an optional fade out time. While the functions logically do the same thing, they're implemented totally differently. Being the same function doesn't make a lot of sense. Even though 0 would be a logical default fade time, it still doesn't make sense to use a default argument here since you would just end up with a big if block:
if (fade_time == 0) {
// body of the first overload
} else {
// body of the second overload
}
I am writing highly generic code, and I am handlying the return value from function calls as const auto&. So for example:
const auto& value = foo();
The purpose is to write it in the most generic way possible, so that it does not make any assumption on the return type of foo, which can return by value or return by reference without breaking anything on the client code even if the value is a temporary.
But can this code perform worst than:
const auto value = foo();
In case foo() is returning a basic type such as int or double or enum?
I understand why you may think it would incur a runtime cost, however, in practice this is not the case.
Any modern compiler will instantly fold this into the value type if the function returns a value type, instead of making it a reference. Your const int& would just become an int after compilation. You can test this out here: https://godbolt.org/g/vT2FdG.
It depends.
In many cases, especially when foo() returns a primitive type, the reference will be optimized away and there will be no difference between both versions.
But if foo() returns a large-ish object, it can depend on whether NRVO is applicable or not.
The construct auto value = foo(); enables NRVO, and const auto& value = foo(); can sometimes prevent it (demo). So the non-reference version can be faster.
But if NRVO isn't possible due to the way foo() is written, a temporary object may be created and copied. So the reference version will be faster.
Note: if the returned object isn't copy-constructible, then the non-reference version will fail to compile.
Your best bet is to trial the results for plain-old-data types, which includes inspecting the generated assembly on your target platforms.
For me, instinct suggests that
const int value = foo();
would be faster than
const int& value = foo();
since the overhead of setting up a reference - including the potential for lifetime extension - may be greater than a value copy of a plain-old-data type.
Where the cutoff is, if any, would be platform-dependent, and your optimising compiler might level the playing field in any case.
Basing this on your comment, the C++17 cookbook solution for working with generic functors that accept forwarded parameters is something like this:
If you need their return value, with value category and what not preserved, use a decltype(auto) variable to capture it.
Have your own functions's return type be decltype(auto).
Use std::invoke to simplify the call site. And to truly support all sorts of curious functor types.
Use std::invoke_result to handle the corner case where the return type is void. You don't want to be defining variables of type void.
All-in-all it makes for this template (of a template):
#include <type_traits>
#include <functional>
#include <utility>
template<class F, typename... Args>
decltype(auto) process_call(F&& f, Args&&... args) {
if constexpr (std::is_void_v<std::invoke_result_t<F, Args...>>) {
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
// Process without capturing
return;
}
else {
decltype(auto) ret = std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
// Use ret for your processing
return ret;
}
}
I want to define a function that takes (besides its usual input arguments) a lambda function. And I want to restrict that function as far as possible (its own input- and return types).
int myfunc( const int a, LAMBDA_TYPE (int, int) -> int mylamda )
{
return mylambda( a, a ) * 2;
}
Such that I can call the function as follows:
int input = 5;
myfunc( input, [](int a, int b) { return a*b; } );
What is the correct way to define myfunc?
And is there a way to define a default lambda? Like this:
int myfunc( const int a, LAMBDA_TYPE = [](int a, int b) { return a*b; });
If you take a std::function<int(int,int)> it will have overhead, but it will do what you want. It will even overload correctly in C++14.
If you do not want type erasure and allocation overhead of std::function you can do this:
template<
class F,
class R=std::result_of_t<F&(int,int)>,
class=std::enable_if_t<std::is_same<int,R>{}>
>
int myfunc( const int a, F&& f )
or check convertability to int instead of sameness. This is the sfinae solution. 1
This will overload properly. I used some C++14 features for brevity. Replace blah_t<?> with typename blah<?>::type in C++11.
Another option is to static_assert a similar clause. This generates the best error messages.
Finally you can just use it: the code will fail to compile if it cannot be used the way you use it.
In C++1z concepts there will be easier/less code salad ways to do the sfinae solution.
1 On some compilers std::result_of fails to play nice with sfinae. On those, replace it with decltype(std::declval<F&>()(1,1)). Unless your compiler does not support C++11 (like msvc 2013 and 2015) this will work. (luckily 2013/3015 has a nice result_of).
There are two alternatives, the type-erasing std::function<signature> forces a particular signature which goes along the lines of your question, but it imposes some additional cost (mainly it cannot be inlined in general). The alternative is to use a template an leave the last argument as a generic object. This approach can be better from a performance point of view, but you are leaving the syntax checking to the compiler (which I don't see as a problem). One of the comments mentions passing a lambda as a function pointer, but that will only work for lambdas that have no capture.
I would personally go for the second option:
template <typename Fn>
int myFunc(const int a, Fn&& f) {
return f(a, a) * 2;
}
Note that while this does not explicitly force a particular signature, the compiler will enforce that f is callable with two int and yields something that can be multiplied by 2 and converted to int.