FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

[SUGGESTION] Add possibility of using lambda in is-statements (inspect and if statements) by filipsajdak · Pull Request #90 · hsutter/cppfront · GitHub

Repository navigation

[SUGGESTION] Add possibility of using lambda in is-statements (inspect and if statements) - #90

Closed
filipsajdak wants to merge 4 commits into
hsutter:mainfrom
filipsajdak:fsajdak-alternatives-with-lambda
Closed

filipsajdak wants to merge 4 commits into
hsutter:mainfrom
filipsajdak:fsajdak-alternatives-with-lambda

Conversation

filipsajdak commented Oct 23, 2022 •
edited
Loading

Copy link
Copy Markdown
Contributor

In https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2392r0.pdf there is an example that uses lambda with is keyword.

cout << inspect i -> std::string {
  is in(1,2) = "1 or 2";
  is in(2,3) = "3";
  is _ = "something else";
};

or

if i is in(10,20) {
  cout << "I between 10 and 20" << endl;
}

I started to experiment with current implementation to achieve the same functionality and I came up with implementation that you can check here: https://godbolt.org/z/464PP7bW6 (working prototype).

Unfortunately lambda with the capture list cannot be argument of the template (only gcc supports that). So, I have created a wrapper that stores a lambda and the arguments that normally will go to the capture list.

#include <utility>
#include <tuple>
#include <iostream>
#include <vector>

constexpr auto less_than = [](auto value) {
    return cpp2::lambda_wrapper([](auto x, auto capture) { return x < capture;}, value);
};

constexpr auto in = [](auto min, auto max) {
    return cpp2::lambda_wrapper([](auto x, auto capture) { 
        return std::get<0>(capture) <= x && x <= std::get<1>(capture);
    }, std::pair(min,max));
};

constexpr auto empty = cpp2::lambda_wrapper([](auto x){
    return std::empty(x);
});

main: () -> int = {
    i := 15;

    if i is less_than(20) {
        std::cout << "less than 20" << std::endl;
    }

    if i is in(10,30) {
        std::cout << "i is between 10 and 30" << std::endl;
    }

    v : std::vector<int> = ();

    if v is empty {
        std::cout << "v is empty" << std::endl;
    }
}

lambda_wrapper shall use std::tuple to store arguments that shall go to capture list but std::tuple is not structural type and cannot be used in that context. There are possible workarounds (https://stackoverflow.com/questions/69194075/structural-tuple-like-wrapping-a-variadic-set-of-template-parameters-values-in) but I decided to propose something simple.

The current implementation can handle lambda that captures:

  • none argument - using std::monostate that is default second template argument type (lambda_wrapper([](auto x){/**/})),
  • one argument - (lambda_wrapper([](auto x, auto capture){/**/}, captured_value)),
  • two arguments - using std::pair (lambda_wrapper([](auto x, auto capture){/**/}, std::pair(/* captured two values */) )),

If more captured values will be needed we need to add tuple-like structure that will be structural type.

On the last push I have made possible using lambda_wrapper in inspect alternatives (by making id-expression ( expression-list ) an valid alternative syntax.

That makes this code works:

std::cout << inspect i -> std::string {
    is less_than(10) = "i less than 10";
    is in(11,20) = "i is between 11 and 20";
    is _ = "i is out of our interest";
} << std::endl;

filipsajdak force-pushed the fsajdak-alternatives-with-lambda branch from 48dbcb3 to aef3217 Compare October 23, 2022 16:21
filipsajdak marked this pull request as draft October 23, 2022 17:44

Copy link
Copy Markdown
Contributor Author

Ah... the change works in if statements but it is not working yet with inspect - the patch to cppfront is needed not to perceive the syntax as an error.

Copy link
Copy Markdown
Contributor Author

On the last push I have added support for using id-expression ( expression-list ) as alternative in the inspect expression.

filipsajdak marked this pull request as ready for review October 23, 2022 20:02
filipsajdak force-pushed the fsajdak-alternatives-with-lambda branch from c088908 to 4c87da8 Compare October 30, 2022 20:00
filipsajdak force-pushed the fsajdak-alternatives-with-lambda branch from 848a996 to cfa2b25 Compare November 26, 2022 13:35

Copy link
Copy Markdown
Contributor Author

I have noticed that my other PRs regarding using is() with multiple types/templates interfere with this PR and I need to update it.

Currently, I am not able to allow generic lambda use as I don't know how to write requires clause for lambda_wrapper that will match them. Now it matches types that matches:

template <typename F, typename Capture = std::monostate>
    requires (
        requires { &F::operator(); }
    )
struct lambda_wrapper;

What is currently possible is visible here:

#include <utility>
#include <tuple>
#include <iostream>
#include <vector>
#include <span>

constexpr auto less_then = [](int value) {
    return cpp2::lambda_wrapper([](int x, int capture) { return x < capture;}, value);
};

constexpr auto in = [](auto min, auto max) {
    return cpp2::lambda_wrapper([](int x, std::pair<int,int> capture) { 
        return std::get<0>(capture) <= x && x <= std::get<1>(capture);
    }, std::pair(min,max));
};

constexpr auto empty = cpp2::lambda_wrapper([](std::span<const int> x){
    return std::empty(x);
});

main: () -> int = {
    i := 15;

    if i is less_then(20) {
        std::cout << "less than 20" << std::endl;
    }

    if i is in(10,30) {
        std::cout << "i is between 10 and 30" << std::endl;
    }

    std::cout << inspect i -> std::string {
        is less_then(10) = "i less than 10";
        is in(11,20) = "i is between 11 and 20";
        is _ = "i is out of our interest";
    } << std::endl;

    v : std::vector<int> = ();

    if v is empty {
        std::cout << "v is empty" << std::endl;
    }
}

The feature is less generic but still useful.

filipsajdak force-pushed the fsajdak-alternatives-with-lambda branch from cfa2b25 to a4b33a2 Compare November 29, 2022 21:22

Copy link
Copy Markdown
Contributor Author

Rebase to main. Make is() function constexpr

Copy link
Copy Markdown
Contributor Author

Might require #86 for some use cases.

hsutter commented Dec 17, 2022

Copy link
Copy Markdown
Owner

Might require #86 for some use cases.

Should these be merged? Should all the is/as PRs be merged (also #79, #106, #108)? (Not suggesting, just asking. As I look through them I'm trying to grok the overlap.)

Copy link
Copy Markdown
Contributor Author

It could be merged. I was not sure if you would be willing to merge all functionalities.

Copy link
Copy Markdown
Contributor

Currently, I am not able to allow generic lambda use as I don't know how to write requires clause for lambda_wrapper that will match them.

Can you give an example of what doesn't work?

Copy link
Copy Markdown
Contributor Author

The goal was to have each functionality separate.

hsutter commented Dec 17, 2022

Copy link
Copy Markdown
Owner

NP - if they don't conflict I'll plan to look at them individually for now. Thx!

Copy link
Copy Markdown
Contributor Author

@JohelEGP labda_wrapper requires

requires { &F::operator(); }

It means that the below code will work in the is statement:

constexpr auto in = [](auto min, auto max) {
    return cpp2::lambda_wrapper([](int x, std::pair<int,int> capture) { // <--- explicit type int
        return std::get<0>(capture) <= x && x <= std::get<1>(capture);
    }, std::pair(min,max));
};

But this will not:

constexpr auto in = [](auto min, auto max) {
    return cpp2::lambda_wrapper([](auto x, auto capture) { // <--- generic lambda
        return std::get<0>(capture) <= x && x <= std::get<1>(capture);
    }, std::pair(min,max));
};

JohelEGP commented Dec 17, 2022 •
edited
Loading

Copy link
Copy Markdown
Contributor

You could move the constraint to the point where you actually have the argument type X with which to call F. Then you could use std::invocable<lambda_wrapper<F, Capture>, X> if the call operator of lambda_wrapper and F are likewise appropriately constrained: https://compiler-explorer.com/z/s3cbfccnh.

Copy link
Copy Markdown
Contributor Author

I will test that direction.

Unfortunately, std::tuple cannot be used. As I described in the PR description:

lambda_wrapper shall use std::tuple to store arguments that shall go to capture list but std::tuple is not structural type and cannot be used in that context. There are possible workarounds (https://stackoverflow.com/questions/69194075/structural-tuple-like-wrapping-a-variadic-set-of-template-parameters-values-in) but I decided to propose something simple.

hsutter commented Dec 22, 2022

Copy link
Copy Markdown
Owner

Thanks! Implemented as a separate commit along the lines discussed in #79.

hsutter closed this in 29fbd92 Dec 22, 2022

Copy link
Copy Markdown
Contributor Author

@hsutter, your change enables using lambda in inspect expressions but not in if statements. My solution worked for both cases ;)

#include <iostream>
#include <vector>

constexpr auto less_then = [](int value) {
    return [=](auto x) { return x < value;};
};

constexpr auto in = [](auto min, auto max) {
    return [=](auto x) { 
        return min <= x && x <= max;
    };
};

constexpr auto empty = [](auto&& x){
    return std::empty(x);
};

main: () -> int = {
    i := 15;

    std::cout << inspect i -> std::string {
        is (less_then(10)) = "i less than 10"; // works
        is (in(11,20)) = "i is between 11 and 20"; // works
        is _ = "i is out of our interest";
    } << std::endl;

    if i is (less_then(20)) { // not working
        std::cout << "less than 20" << std::endl;
    }

    if i is (in(10,30)) { // not working
        std::cout << "i is between 10 and 30" << std::endl;
    }

    v : std::vector<int> = ();

    if empty(v) { // works
        std::cout << "v is empty" << std::endl;
    }

    if v is (empty) { // not working
        std::cout << "v is empty" << std::endl;
    }
}

hsutter added a commit that referenced this pull request Dec 23, 2022

hsutter commented Dec 23, 2022

Copy link
Copy Markdown
Owner

Ah, you noticed that. :) That was next on my to-do list, and I just checked it in... the above code now works with commit 327ceeb .

filipsajdak deleted the fsajdak-alternatives-with-lambda branch January 12, 2023 21:22
Azmah-Bad pushed a commit to Azmah-Bad/cppfront that referenced this pull request Feb 24, 2023
Azmah-Bad pushed a commit to Azmah-Bad/cppfront that referenced this pull request Feb 24, 2023
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants


Back | FazBrowse Home | New Git URL