lambdas

Status
Not open for further replies.

adrian

Well-known member
Hello

I have this strange error when compiling the following:

Code:
#undef max
#undef min
#include <functional>

bool rule0 (std::function<bool()> application = []() -> bool {return true;});

bool rule0 (std::function<bool()> application = []() -> bool {return true;}) {
      if (application()) {return true;}
      else {return false;}
    }

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:
rule0();
}

I get

Code:
exit status 1
default argument given for parameter 1 of 'bool rule0(std::function<bool()>)' [-fpermissive]

There are other permissive complaints about "after previous specification".

The thing is, I thought that a default argument for parameter 1 was exactly what I was doing, and rather than 'permit' its correct c++ code per se.

If I remove the declaration / prototype of rule0() ...which you think would work fine .... I get a bad call thrown...as if the std::function is not capturing the target lambda.

What is gong on? Is it safe to ignore the permissive error? I guess it is something I can diable in the compiler :settings"?
 
If you already have a function prototype, you can't declare the default parameter again.

By default, the C++ standard library isn't linked, so you must add "-lstdc++" to the library list (e.g. "teensy31.build.flags.libs") in "arduino/hardware/teensy/avr/boards.txt".

You also need an implementation for "_write()" and "abort()". "abort()" is called when normally the C++ standard library would throw an exception.

Code:
#undef max
#undef min
#include <functional>

bool rule0 (std::function<bool()> application = []() -> bool {return true;});

bool rule0 (std::function<bool()> application) {
    if (application()) {return true;}
    else {return false;}
}

void setup() {
}

void loop() {
    rule0();
}

extern "C"{ 
    int _write();
}

extern "C"{
    int _write(){return -1;}
}

void abort() {
    while(1);
}
 
thanks ....I have linked the library. When I remove the declaration, I get "std::bad_function_call", as if the std::function is not capturing the target lambda. I can handle that exception easily enough, but why is it getting thrown? I tried another approach

Code:
auto default_parameter = []() { return true; };

template<
    typename Functor = decltype(default_parameter)
>
void rule0(Functor f = default_parameter);

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:
rule0();
}

but that gives

Code:
void rule0(Functor) [with Functor = __lambda0]', declared using local type '__lambda0', is used but never defined [-fpermissive]

Why should there be any error at all with that code
 
thanks ....I have linked the library.
No, probably not.
When I remove the declaration, I get "std::bad_function_call", as if the std::function is not capturing the target lambda. I can handle that exception easily enough, but why is it getting thrown?
You are getting a linker error, right?
.../functional:2266: undefined reference to `std::__throw_bad_function_call()'

That means you didn't link "libstdc++".

I tried another approach

Code:
auto default_parameter = []() { return true; };

template<
    typename Functor = decltype(default_parameter)
>
void rule0(Functor f = default_parameter);

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:
rule0();
}

but that gives

Code:
void rule0(Functor) [with Functor = __lambda0]', declared using local type '__lambda0', is used but never defined [-fpermissive]

Why should there be any error at all with that code
You can't have plain function templates in a sketch. The brain-dead Arduino preprocessor screws them up. You have to put them into a class or namespace. You haven't defined rule0().

This compiles:

Code:
auto default_parameter = []() { return true; };

namespace the_arduino_preprocessor_is_buggy {
template<
    typename Functor = decltype(default_parameter)
>
void rule0(Functor f = default_parameter) {
    f();
}
}
using namespace the_arduino_preprocessor_is_buggy;

void setup() {}

void loop() {
    rule0();
}

This code does something very different from your std::function version. Each lambda is a unique type, so you are restricted to compile time polymorphism, as opposed to the run-time polymorphism you get with std::function.
 
Thanks so much for this .... I'll digest ...I can kind of see the difference between runtime and comiple time polymorphism, and I don't think it is anissue for my program. Thanks so much for helping out. I'm just starting with all this c++ stuff.
 
Hey tni .... awesome help there buddy!!

I see what you mean about runtime vs compile time .... run time binding is what I need, to keep everything encapsulated how I want, as it turns out. Forced me to re-evaluate some design decisions, so I have a bit of refactoring to do. You can't capture "this" in a lambda default parameter, by the looks ..... makes sense, I suspect. But not quite in tune with a design decision I made.

Anyway, it all compiles nicely now, thanks to you ... you were right, I hadn't done the lstdc++ thing.
 
Last edited:
Thought I'd post... you can't capture "this" in the lambda default parameter per se ... makes sense. But you can rearrange things ... I haven't tested this code, since I don't have a teensy at the moment!!! but it should print "5" ...it compiles (once you change the teensy compiler settings) ....thanks again tni. The hint about late binding polymorphism helped me work out the encapsulation scheme I needed. There is an awful lot of memory overhead associated with using standard functions by the looks, but it seems to be a fixed overhead (not sure about that though) ..

Code:
#include <functional>

struct Rule {
  int x = 5;
  std::function <int()> func = [this]() -> int {return x;};
} rule;

class Substantive {
  public:
    int rule1 (std::function <int()> func = rule.func) {
      return func();
    }
} substantive;

void setup() {
  Serial.begin (9600);
}

void loop () {
  Serial.print ( substantive.rule1() );
}

extern "C"{ 
    int _write();
}

extern "C"{
    int _write(){return -1;}
}

void abort() {
    while(1);
}
 
There is an awful lot of memory overhead associated with using standard functions by the looks, but it seems to be a fixed overhead (not sure about that though) ..
If you don't build optimized for size ("smallest code" IDE option), a version of libstdc++ with exception handling enabled is linked. That pulls in a huge amount of unnecessary stuff. The magic option in "boards.txt" is "--specs=nano.specs" added to the build flags.

std::function has more overhead than using a virtual function, but it's usually not too bad. GCC optimizes better with virtual functions and may add an optimized inlined fast path, if it knows the likely call target.

E.g. a std::function call:
Code:
test(int):
        push    {r0, r1, r2, lr}
        str     r0, [sp, #4]
        ldr     r0, .L17
        ldr     r3, [r0, #8]
        cbnz    r3, .L16
        bl      std::__throw_bad_function_call()
.L16:
        ldr     r3, [r0, #12]
        add     r1, sp, #4
        blx     r3
        add     sp, sp, #12
        ldr     pc, [sp], #4

Virtual function:
Code:
test2(int):
        ldr     r3, .L20
        ldr     r3, [r3]
        ldr     r2, [r3]
        mov     r1, r0
        ldr     r2, [r2]
        mov     r0, r3
        bx      r2

The test code.
 
If you don't build optimized for size ("smallest code" IDE option), a version of libstdc++ with exception handling enabled is linked. That pulls in a huge amount of unnecessary stuff. The magic option in "boards.txt" is "--specs=nano.specs" added to the build flags.

std::function has more overhead than using a virtual function, but it's usually not too bad. GCC optimizes better with virtual functions and may add an optimized inlined fast path, if it knows the likely call target.

E.g. a std::function call:
Code:
test(int):
        push    {r0, r1, r2, lr}
        str     r0, [sp, #4]
        ldr     r0, .L17
        ldr     r3, [r0, #8]
        cbnz    r3, .L16
        bl      std::__throw_bad_function_call()
.L16:
        ldr     r3, [r0, #12]
        add     r1, sp, #4
        blx     r3
        add     sp, sp, #12
        ldr     pc, [sp], #4

Virtual function:
Code:
test2(int):
        ldr     r3, .L20
        ldr     r3, [r3]
        ldr     r2, [r3]
        mov     r1, r0
        ldr     r2, [r2]
        mov     r0, r3
        bx      r2

The test code.

Thanks ...very interesting ... I will have to digest. Thanks for the compiler flag help. very much appreciated. I'm not sure that virtual functions are flexible enough for what I am doing... I like the functional approach without the demands that an inheritance scheme impose .... passing functions around my (ever growing) set of class objects using std::function is working for me at the moment. Glorified function pointers, really ..... kind of LISP-inspired..... I haven't run out of memory on my teensy 3.2 yet, but it won't be long (I have a large unwritten dataset to incorporate yet ...that should tip things over the edge).... time for a teensy 3.6 .... Thanks again for all your help.
 
Last edited:
Status
Not open for further replies.
Back
Top