简体   繁体   中英

translate python function with yield(string) to C++

I'm translating a python function into a C++ function; this function uses a yield(string) statement that I don't know how to translate.

Here the whole story...I have a certain function that reads an input ASCII file filename , which contains lines of data, and puts the content of the file into an std::vector . When reading this input file (in another function respect with the one showed below), I need to jump a bunch of lines (5 or 6, it depends on the input file's name) and, for this purpose, I define a function, the file_struct_generator , which marks as "data" the data lines and as "unused" the data lines I don't need. I need something similar to this function in C++, and in particular, I need something similar to yield(string) (pay attention, string !), but in C++ of course. Here I show you the lines of the code I need to "translate" from python to C++. How can I rewrite yield("unused") and yield("data") in C++? Or if yield is unusable in C++, can I write a similar function in C++ using something different which works as a generator? Thanks for helping me!

def file_struct_generator(filename):
    if "more" in filename:
        bad_line_number = 5
    else:
        bad_line_number = 6

    yield("data")
    for i in range(bad_line_number):
        yield("unused")
    while True:
        yield("data")
    

EDIT: I do not use C++20, but C++11. I tried @Arthur Tacca code and it works for my purpose, thank you all.

translate python function with yield(string) to C++

Prior to C++20, I don't think there is a simple, exact translation.

An alternative is to write a function template that accepts a functor:

template<class Yield>
void file_struct_generator(const std::string& filename, const Yield& yield)
{
    // ...
    yield("unused")
    // ...

This way the caller can provide a functor that deals with the output in a way they want to use it. They can provide for example one that prints the values, or another that stores the values in a container.

Crucially, this alternative is not lazy, and you cannot have an infinite loop like in the python code. Whether this is a useful translation depends on how the generator is used in Python. This is good for cases where the entire sequence is consumed.


I think this would be a direct translation in C++20:

generator<std::string>
file_struct_generator(const std::string& filename)
{
    bad_line_number = filename.find("more") != std::string::npos
        ? 5
        : 6;

    co_yield "data";
    for (int i : std::views::iota(0, bad_line_number))
        co_yield "unused";
    for(;;)
        co_yield "data";
}

Not tested due to lack of conforming compilers. Also the standard doesn't provide the generator type.

This is a state machine that reproduces that generator's functionality.

#include <stdexcept>
#include <string>
#include <iostream>

class FileStructIterator {
public:
    explicit FileStructIterator(const std::string& filename): 
        m_filename(filename), m_state(STATE_START) 
    {}

    std::string getNext() {
        switch (m_state) {
        case STATE_START:
            m_state = STATE_LOOP1;
            if (m_filename.find("more") != std::string::npos) {
                m_badLineNumber = 5;
            } else {
                m_badLineNumber = 6;
            }
            m_i = 0;
            return "data";

        case STATE_LOOP1:
            ++m_i;
            if (m_i >= m_badLineNumber) {
                m_state = STATE_LOOP2;
            }            
            return "unused";

        case STATE_LOOP2:
            return "data";

        default:
            throw std::logic_error("bad state");
        }
    }

private:
    std::string m_filename;

    enum State { STATE_START, STATE_LOOP1, STATE_LOOP2 };
    State m_state;

    size_t m_badLineNumber;
    size_t m_i;
};

Here's an example of using it (in this case I limited the output to the first 10 results so it doesn't loop forever).

int main() {
    auto it = FileStructIterator("nomore.txt");
    for (int i = 0; i < 10; ++i) {
        std::string nextValue = it.getNext();
        std::cout << nextValue << "\n";
    }
}

From C++20 you can use co-routines, you can achieve similar functionality to the python snippet with some thread_local variables (that are static):

std::string file_struct_generator(std::string filename) 
{
  thread_local int bad_line_number = filename.find("more") != std::string::npos ? 5 : 6;

  thread_local int i = 0;

  if (i++ && i <= bad_line_number) 
    return "unused";

  return "data";
}

Note that, as pointed out by ArthurTacca in the comments, this function can't actually be called with a different file-name, or at least, it won't start a new loop if you do that.

In C++20 this would use co_yield . Prior to that you can write an iterator.

class file_struct_iterator : public std::iterator<std::input_iterator_tag, std::string, std::ptrdiff_t, const std::string *, std::string> {
    enum class State {
        initial,
        bad_lines,
        remainder
    }

    State state = State::initial;
    size_t bad_line_number = 0;

public:

    file_struct_iterator(std::string filename) 
        : bad_line_number((filename.find("more) != filename.end()) ? 5 : 6)
    {}

    std::string operator*() {
        switch (state) {
            case State::initial:
            case State::remainder: return "data";
            case State::bad_lines: return "unused";
        }
    }

    file_struct_iterator& operator++() {
        switch (state) {
            case State::initial: state = State::bad_lines; break;
            case State::bad_lines: if (--bad_line_number == 0) { state = remainder; } break;
            case State::remainder: break;
        }
        return *this;
    }

    file_struct_iterator operator++(int) {
        file_struct_iterator retval = *this;
        ++(*this);
        return retval;
    }

    bool operator==(file_struct_iterator other) const {
        return (state == other.state) && (bad_line_number == other.bad_line_number);
    }
    
    bool operator!=(file_struct_iterator other) const {
        return !(*this == other);
    }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM