Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Empty generator #210

Open
rafalg opened this issue Oct 26, 2024 · 6 comments
Open

Empty generator #210

rafalg opened this issue Oct 26, 2024 · 6 comments

Comments

@rafalg
Copy link

rafalg commented Oct 26, 2024

Hi,

Thanks for creating cobalt. Feels like python asyncio but in c++.
Been trying to use generators to model results of paginated api like aws s3::list_objects.
All in all seems pretty strightforward , I can co_yeild individual response elements and co_return the last one.
But came across a corner case where api will respond with 0 elements. My outer loop is:

auto keys = s3.list_objects("test-bucket");

while (keys) {
  auto key = co_await keys;
}

In python when list_objects goes out of scope simply terminates generator.
In cobalt I got the

  what():  cobalt::generator returned void

Any help appreciated!

@klemens-morgenstern
Copy link
Collaborator

You're missing a co_return at the end. That's technically UB, but it works akin to how python would throw a StopIteration.
You can either handle that exception or co_return a null value.

@rafalg
Copy link
Author

rafalg commented Oct 27, 2024

Totally understood. What I meant is that it forces the generator client code to handle special value like null/empty optional as an additional condition to break the iteration. Would you consider adding such a feature to the cobalt ifself? Following python example maybe something like cobalt_iteration_break that could be thrown from generator impl. Cobalt could catch it and nicely terminate the for loop on the client side.

Im working a lot with paginated apis. Im wrapping them in generators so that the client code can work with continous the stream of data without knowing how many calls have been done to the server to acually fetch them. Adding these special values to handle cases like empty api responses etc is just a bit annoying.

@klemens-morgenstern
Copy link
Collaborator

What's the annoying part? An extra branch for co_return in the generator?

@rafalg
Copy link
Author

rafalg commented Nov 13, 2024

The annoying part is extra condition I have to put in the client code.
So instead of

while (g) {
  auto x = co_await g;
 ...
}

I have to

while (g) {
  auto x_wrapped_in_optional = co_await g;
  if (!x) {
    break;
  }
  auto x = *x;
 ...
}

Easy to forget, plus and additional case to cover in tests. Also everything returned from generator has to be wrapped into std::optional or something similar to allow this check to happen.

@klemens-morgenstern
Copy link
Collaborator

Ok, but can't you just throw from the generator then? Because by the time you call co_await the generator doesn't know if it's completed or not.

@rafalg
Copy link
Author

rafalg commented Nov 13, 2024

Yeah I know I can throw. But it seems like another work around. Haven't done c++ for a while so don't know the limitations or if its possible at all. coming straight from python asyncio it just feels a bit arkward. Implementing stuff like database queries or paginated apis with generators seems kind a natural. Having to add these null checks or try/catch for a pretty simple case like query with no results is just a bit annoying :).

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

No branches or pull requests

2 participants