josuegomes
josuegomes

Reputation: 501

Boost.Cobalt: how to exit from a generator without returning a value

I'm converting this code that uses Lewis Baker's cppcoro library to use Boost.Cobalt

#include <cppcoro/generator.hpp>

#include <iostream>
#include <fstream>
#include <string>

cppcoro::generator<std::string> read_script_file(std::ifstream& script)
{
    std::string line;
    while (std::getline(script, line)) {
        if (line.empty()) {
            continue;
        }
        if (line[0] == '#') {
            continue;
        }
        co_yield line;
    }
}

void print_script_file()
{
    std::ifstream ifs("script");

    for (const auto line : read_script_file(ifs)) {
        std::cout << line << "\n";
    }
}

And this is the Boost.Cobalt version:

#include <boost/cobalt.hpp>

#include <fstream>
#include <iostream>
#include <string>

boost::cobalt::generator<std::string> read_script_file(std::ifstream& script)
{
    std::string line;
    while (std::getline(script, line)) {
        if (line.empty()) {
            continue;
        }
        if (line[0] == '#') {
            continue;
        }
        co_yield line;
    }
    co_return "--exit";
}

void print_script_file()
{
    std::ifstream ifs("script");

    while (true) {
        const auto line = co_await read_script_file(ifs);
        if (line == "--exit") {
            break;
        }
        std::cout << line << "\n";
    }
}

The cppcoro version is much more elegant as it doesn't need to return a "--exit" string to finish the generator. Is there a better way to write the Boost.Cobalt version?

[1] cppcoro - https://github.com/lewissbaker/cppcoro

[2] Boost.Cobalt - https://github.com/boostorg/cobalt

Upvotes: 1

Views: 265

Answers (1)

josuegomes
josuegomes

Reputation: 501

Answering my own question. Boost.Cobalt author provided a reply [1].

In summary, returning no value from that coroutine is UB (undefined behavior).

This is the final, complete example:

#include <boost/cobalt.hpp>
#include <boost/cobalt/main.hpp>

#include <fstream>
#include <iostream>
#include <string>

boost::cobalt::generator<std::string> read_script_file(std::ifstream& script)
{
    std::string line;
    while (std::getline(script, line)) {
        if (line.empty()) {
            continue;
        }
        if (line[0] == '#') {
            continue;
        }
        co_yield line;
    }
    co_return {};
}

boost::cobalt::main co_main(int argc, char *argv[])
{
    try {
        std::ifstream script("script");
        if (!script) {
            std::cerr << "ERR: failed to open " << argv[1] << "\n";
            co_return 1;
        }

        auto g = read_script_file(script);
        while (g) {
            const auto line = co_await g;
            if (line.empty()) {
                break;
            }
            std::cout << "<" << line << ">\n";
        }
    }
    catch (const std::exception& e) {
        std::cout << "Exception: " << e.what() << "\n";
    }

    co_return 0;
}

[1] https://github.com/boostorg/cobalt/issues/176#issuecomment-2073694105

Upvotes: 1

Related Questions