fakedrake
fakedrake

Reputation: 6856

C++ Inline lambda argument

Consider the followng code

#include <iostream>
#include <functional>

using namespace std;

inline void readandrun(function<void(int)> callback) {
    int i;
    i = 1;
    callback(i);
}

int main(int argc, char *argv[])
{
#ifdef LAMBDA
    readandrun([](int i){ printf("the read number is: %d\n",i);});
#else
    int i;
    i = 1;
    printf("the read number is: %d\n",i);
#endif
    return 0;
}

Compiling with

g++ -DLAMBDA -O2 -std=c++17 -S test.cpp -o test_long.S 

Yields code involving jumps while

g++ -O2 -std=c++17 -S test.cpp -o test_short.S 

Does not. Which kind of makes sense but is it possible to tell the compiler to inline the lambda argument since it's known at compile time? I would be willing to switch compilers but for completeness:

$ g++ --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

Upvotes: 4

Views: 1224

Answers (2)

Fureeish
Fureeish

Reputation: 13424

[...] is it possible to tell the compiler to inline the lambda argument since it's known at compile time?

Highly unlikely (in other words - no), which is mainly because you use std::function which, due to it's implementation's nature, uses type erasure and achieves this with dynamic allocation. When you call it, a virtual call is made so there is a jump. In order to inline the code, you may switch to:

template <typename Callback>
inline void readandrun(Callback callback) {
    int i;
    i = 1;
    callback(i);
}

That way, the lambda's exact type is deduced and the code is perfectly fine for inlineability.


Remember - lambda's type is never std::function. It's (the std::function) merely a wrapper around a callable with actual type erased.

Upvotes: 10

Yksisarvinen
Yksisarvinen

Reputation: 22219

If you can allow to take function pointer instead of std::function as your argument, you can make your function constexpr:

constexpr void readandrun(void(*callback)(int)) {
    int i = 1; // can't use uninitialized variables in constexpr function
    callback(i);
}

See the assembly online

Note that only non-capturing lambdas can be converted to function pointers. For capturing lambdas see Fureeish's answer. Also, constexpr functions have some limitations, but on the other hand compiler will execute them at compile time if possible.

Upvotes: 5

Related Questions