1

The textbook functional programming introduction example "return a function with a curried parameter" in C++ does not compile for me:

// return a function x(v) parameterized with b, which tells if v > b
bool (*greater(int))(int b) 
{
    return [b](int v) { return v > b; };
}

It says that identifier b in the capture [b] is undefined. I know that I'm being naive here, but where is my error?

EDIT: as @some-programmer-dude pointed out correctly, the function signature is wrong.

greater is a function accepting an int b returning ( a pointer * to a function accepting an (int) returning a bool ).

// return a function x(v) parameterized with b, which tells if v > b
bool (*greater(int b))(int) 
{
    return [b](int v) { return v > b; };
}

This of course does not remove the original question which all three replies answered correctly.

Vroomfondel
  • 2,704
  • 1
  • 15
  • 29
  • 4
    Only lambdas **without capture** can be cast into a C style function pointer. Lambdas with capture are actually classes with data members (one for each capture). In your case you have one capture (`b`). – wohlstad Oct 20 '22 at 16:34
  • 1
    Your declaration for returning a function pointer is wrong (the b should beside the first int) `bool (*greater(int b))(int v)`. Some `using` statements to simplify that type might make that clerer. But even so, you can't convert a lambda into a function pointer (apart from the most trivial of lambda's with not special features). – Martin York Oct 20 '22 at 16:38
  • can't convert a **capturing** lambda into a function pointer. Plain lambdas do just fine in that regard. – sweenish Oct 20 '22 at 16:39
  • 1
    [Dupe1](https://stackoverflow.com/questions/74128124/c-pass-a-curried-function-to-a-function-which-receives-a-pointer) , [Dupe2](https://stackoverflow.com/questions/28746744/passing-capturing-lambda-as-function-pointer) – Jason Oct 21 '22 at 04:00
  • @JasonLiam this question is regarding the computer science concept of currying, and not only the technical issue of converting lambdas to fucntion pointers. The links you posted do not really address this issue (the answers focus on the technical issue). Please consider to re-open the question. – wohlstad Oct 21 '22 at 04:15
  • @wohlstad The fundamental problem is the same which is that "a capturing lambda cannot be converted into a function pointer".This question can be answered by any of the dupes. That is the main thing here.Just because OP mentioned currying doesn't mean the problem isn't due to a capturing lambda. You can add many different terms in the question like `templates`, `inference` etc but that will still mean that the problem is due to a capturing lambda which is explained in any of the dupes.I am sure there are even more dupes for this but I think the current one is enough. Feel free to suggest more. – Jason Oct 21 '22 at 04:16
  • 1
    @JasonLiam I believe that although the core of StackOverflow is to supply immediate answers to technical questions, people can benefit from some broader context and overview of relevant issues. I think currying and closure are such issues and the OP is clearly interested in them (which is why I gave some details and links about them in my answer). – wohlstad Oct 21 '22 at 04:55
  • @JasonLiam the dupes were not proposed to me by SO when I was typing the question. Usually I am happy to avoid dupes if I can but obviously a guy with little technical knowledge en detail of C++ but with a theoretical background *does not employ* the right terms to describe the problem. – Vroomfondel Oct 21 '22 at 08:48

3 Answers3

4

You say that greater is a function taking an unnamed (anonymous) int argument, and return a pointer to a function taking an int argument with the name b.

The part (int b) is the argument list for the returned function pointer.

To solve that specific problem use

bool (*greater(int b))(int)

instead.

Because function pointers are so complicated, they are usually hidden behind type-aliases:

using greater_function_type = bool(int);
greater_function_type* greater(int b) { ... }

As for the problem that lambdas with captures can't be used as C-style function pointers, use std::function as return type instead:

using greater_function_type = std::function<bool(int)>;
greater_function_type greater(int b) { ... }

Note that it's not returning a pointer anymore.

Since the C++14 standard you can use automatic return-type deduction with the keyword auto:

auto greater(int b) { ... }

If you need to store the returned objects, for example as a class member variable, you need to use std::function<bool(int)> for that.


Also be careful when using names like greater in conjunction with using namespace std; (which is a bad habit), because of std::greater.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Gahh! You got me there - I really goofed up the return value. Well done - in fact function pointers are so complicated that all other answers missed this second error :)). Time to brush up my C skills again - or convince mgmt to move to Rust. – Vroomfondel Oct 21 '22 at 08:42
3

Lambda is capturing [b], which cannot be converted to a function pointer. It requires std::function:

std::function<bool(int)> greater (int b) { ... }

If the function is expected to be inline then you may use simple auto as well:

auto greater (int b) { ... }

BTW, if b is not expected to change within the scope of greater(), then accept it as const int b instead f int b to be more expressive.

iammilind
  • 68,093
  • 33
  • 169
  • 336
2

In order to implement function currying, you need to use the computer-science concept of closure. One of the ways to achieve it in C++ is using capturing lambdas (like you did in your code).

But only C++ lambdas without capture can be cast into a C style function pointer (as you attempted).
Lambdas with capture are actually classes with data members (one for each capture).
In your case you have one capture (b).

In order to return a curried function (which require a capture) you should use std::function:

#include <functional>
#include <iostream>

std::function<bool(int)> greater(int b)
{
    auto l = [b](int v) { return v > b; };
    return l;

}

int main()
{
    auto g5 = greater(5);
    std::cout << g5(2) << std::endl;
    std::cout << g5(7) << std::endl;
}

Output:

0
1
wohlstad
  • 12,661
  • 10
  • 26
  • 39