0

I am trying to generalize my benchmarking function, by having it receive the function to benchmark as the first parameter and the number of iterations as the second.

But since the function to benchmark needs to receive additional parameters, I thought I would fill in the parameters in the body of a lambda function and pass that to the benchmarking function. (I think that is called currying?)

Anyway I can not get it to compile:

main.cpp:43:62: error: invalid initialization of non-const reference of type ‘std::function<double*()>&’ from an rvalue of type ‘main(int, char**)::<lambda()>’
   bench::bench([=](){rng::gpu_r_exp((int) 10e6, lambda);}, 50);

The function declaration looks like this:

void bench(std::function<double*()>& funct_to_bench, int repeats);

and I use it like this:

bench::bench([=](){rng::gpu_r_exp((int) 10e6, lambda);}, 50);

Since the compiler bickers about non-const again, I should maybe add, that gpu_r_exp utilizes a global variable which stores the rngState (it also did not like non-const parameters in gpu_r_exp).

I am really stuck. I just want to fill in the parameters and pass the pre-prepared function handle to the benchmarking function, so that it can wrap a timer with a progress bar around it.

EDIT: I should add that the parameter called lambda is a double, which is the parameter of the exponential distribution and has nothing to do with the lambda function.

Remy Lebeau
  • 454,445
  • 28
  • 366
  • 620
Felix B.
  • 349
  • 2
  • 13
  • A lambda is not a `std::function`. You need to pass a `std::function` to the function since it requires a non-const lvalue reference. – NathanOliver May 26 '20 at 19:20
  • @NathanOliver https://stackoverflow.com/questions/6458612/c0x-proper-way-to-receive-a-lambda-as-parameter-by-reference the accepted answer says that option 1 works, and that is pretty much what I used, right? – Felix B. May 26 '20 at 19:24
  • 2
    Your function is using a `std::function &`, that's not the same as a `std::function`. non-const lvalue references (which `std::function &` is) do not allow for conversions. You have to give it exactly what it is. – NathanOliver May 26 '20 at 19:25
  • @NathanOliver what is an `lvalue reference`? – Felix B. May 26 '20 at 19:25
  • I'm confused how the `const`-ness of the function parameter relates to a global variable you have – UnholySheep May 26 '20 at 19:26
  • @UnholySheep I first tried to pass the RngState as a const parameter since it complained about all parameters which were not const. But since it has setters (you have to change the rng state when you used it), it did complain about that. So I refactored it to a global variable. I wonder whether it still complains about that since the message includes `const`. I don't really understand the error message beyond that – Felix B. May 26 '20 at 19:28
  • 1
    @FelixB. In `void foo(int&)`, the `int&` is a non-const lvalue refernce. In `void foo(const int &)` the `const int &` is a const lvalue reference. In `void foo(int&&)` the `int&&` is a rvalue reference. In `void foo(const int&&)` the `consat int&&` is a const rvalue reference. – NathanOliver May 26 '20 at 19:28
  • @NathanOliver I pretty much just copied `void f(std::function< int(int) >& lambda);` from the linked question. I will look up what lvalues and rvalues are I guess. But shouldn't the same syntax work? – Felix B. May 26 '20 at 19:31
  • 1
    @FelixB. No. Like I said: non-const lvalue references do not allow for conversions. You have to give it exactly what it is. Since a lambda is not a `std::function` it wont work. A const-lvalue reference does allow for conversions and will let the code work. – NathanOliver May 26 '20 at 19:34
  • @NathanOliver `main.cpp:43:62: error: invalid initialization of reference of type ‘const std::function&’ from expression of type ‘main(int, char**)::’ bench::bench([=](){rng::gpu_r_exp((int) 10e6, lambda);}, 50);` – Felix B. May 26 '20 at 19:43
  • `std::function` says the function should return a `double*` and take no arguments. `[=](){rng::gpu_r_exp((int) 10e6, lambda);}` does not do that. It doesn't take any parameters so you are good there, but it doesn't return anything so it has the signature `void()` – NathanOliver May 26 '20 at 19:46
  • Oh right, I am sorry `main.cpp:43:62: error: invalid initialization of reference of type ‘const std::function&’ from expression of type ‘main(int, char**)::’ bench::bench([=](){rng::gpu_r_exp((int) 10e6, lambda);}, 50);` I forgot that I am eating the return value with the lambda too – Felix B. May 26 '20 at 19:50
  • `void*` is a pointer to void. you want just `std::function` – UnholySheep May 26 '20 at 20:10
  • Same problem. `std::function` takes a function that returns a `void*` pointer, but your lambda does not return anything at all so its return value is `void` instead. – Remy Lebeau May 26 '20 at 20:10
  • this worked .... thanks guys - I guess I am too tired for this. I should get some sleep – Felix B. May 26 '20 at 20:15

1 Answers1

1

Given that the benchmark wrapper is small, it doesn't make sense to worry about what's passed in, or whether it can be converted to std::function. Just take what comes and as long as it can be called, you're golden:

template <typename Fun>
void benchmarkCallable(size_t iterations, Fun &&callable)
{
  //...
  while (iterations--)
    callable();
  //..
}

If you worry that the //... sections are getting unwieldy, you can factor them out into a class:

class ScopedBenchmark {
  // start time, other state needed, etc.
public:
  ScopedBenchmark() { /* capture initial state */ }
  ~ScopedBenchmark() { /* dump results etc */ }
};

template <typename Fun>
void benchmarkCallable(size_t iterations, Fun &&callable)
{
  ScopedBenchmark benchmark;
  while (iterations--)
    callable();
}

int main()
{
  benchmarkCallable(1'000'000, []{ printf("foo!\n"); });
}
Kuba hasn't forgotten Monica
  • 88,505
  • 13
  • 129
  • 275