tags:

views:

599

answers:

2

I sort of understand this, at least the function of generators (I've used them in Python). I understand how the switch statement and its content is formed. However, I get these errors.

test.cpp: In constructor 'Foo::descent::descent(int)':
test.cpp:46: error: invalid use of nonstatic data member 'Foo::index_'
test.cpp: In member function 'bool Foo::descent::operator()(std::string&)':
test.cpp:50: error: invalid use of nonstatic data member 'Foo::bars_'
test.cpp:50: error: invalid use of nonstatic data member 'Foo::index_'
test.cpp:51: error: invalid use of nonstatic data member 'Foo::index_'
test.cpp:51: error: invalid use of nonstatic data member 'Foo::bars_'
test.cpp:52: error: invalid use of nonstatic data member 'Foo::index_'

Here's the code. If you have a better way of dealing with this, by all means share please.

#include <math.h>
#include <string>
#include <vector>
#include <iostream>

#ifndef __generator_h__
#define __generator_h__

// generator/continuation for C++
// author: Andrew Fedoniouk @ terrainformatica.com
// idea borrowed from: "coroutines in C" Simon Tatham,
//                     http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

struct _generator
{
  int _line;
  _generator():_line(0) {}
};

#define $generator(NAME) struct NAME : public _generator

#define $emit(T) bool operator()(T& _rv) { \
                    switch(_line) { case 0:;

#define $stop  } _line = 0; return false; }

#define $yield(V)     \
        do {\
            _line=__LINE__;\
            _rv = (V); return true; case __LINE__:;\
        } while (0)
#endif

class Foo {
    int index_;
    std::vector<std::string> bars_;
    public:
    Foo() {
        index_ = 0;
        bars_.push_back("Foobar");
        bars_.push_back("Barfoo");
    }
    $generator(descent){
        int j;
        descent(int j) {
            index_+=j;
        }
        $emit(std::string)
            while(true) {
                $yield(bars_[index_++]);
                if(index_ >= bars_.size())
                    index_ = 0;
            }
        $stop;
    };
    //descent bar;
    void InitGenerator() { index_ = 0; }
};

using namespace std;

int main()
{
  //Foo::descent gen(1);
  //for(int n; gen(n);) // "get next" generator invocation
  //  cout << n << endl;
  return 0;
}
+4  A: 

I'm not entirely sure what you're going for here, but here's where your error is occuring:

Let's expand the macros to see how this really looks:

class Foo {
    int index_;
    std::vector<std::string> bars_;
    public:
    Foo() {
        index_ = 0;
        bars_.push_back("Foobar");
        bars_.push_back("Barfoo");
    }
    struct descent: public _generator {
        int j;
        descent(int j) {
            index_+=j;
        }
        bool operator()(std::string& _rv) {
         switch(_line) { case 0:;
            while(true) {
                do {
                    _line=__LINE__;
                    _rv = (bars_[index_++]); return true; case __LINE__:;
                } while (0);
                if(index_ >= bars_.size())
                    index_ = 0;
            }
         } _line = 0; return false; }
    };
    //descent bar;
    void InitGenerator() { index_ = 0; }
};

As you can see, we declare an inner structure Foo::descent. However, unlike in some other languages, inner classes in C++ do not automatically have a pointer to an instance of their outer class. You need to either add to descent a Foo * which is passed in via the descent constructor, and use that Foo * to reference index_ and bars_ - or move the necessary members right into descent.

To be honest, I don't really understand what Foo here is for at all... Everything it does seems to belong right in descent.

bdonlan
Well, I need to be able to reinitialize descent objects, but retain the state of index_. It can't be static, because there will be multiple instances of Foo.
Scott
That worked, passing Foo *.
Scott
@Scott Your comment seems to contradict your code, it looks like descent(int j) is modifying only index ... but you said you want to keep the index and reset the rest ... Still your code is too complex for the simple thing it is supposed to do. Switch to C++ or move on to Python.
stefanB
+5  A: 

Is this what you were after?

I'm not quite sure what you wanted the generator to return but please modify as you wish.

The idea is that the generator object keeps it's own state and when you call method on it it gives you back next value. It is completely up to you what you define as the state and next value to be returned.

The operator() can accept parameters, as in operator()(bool b) or operator()(char * c), as well as return whatever value you want ...

#include <iostream>
#include <vector>
#include <string>
using namespace std;

struct generator
{
    generator() : currentCh(0), currentW(0), str(0), words()
    {   // do whatever initialization you need
        words.push_back("Foobar");
        words.push_back("Barfoo");
        str = &words[currentW];
    }

    char operator()()
    { // use whatever logic you need
        if (currentCh >= str->size())
        {
            if (++currentW >= words.size()) currentW = 0;
            str = &words[currentW];
            currentCh = 0;
        }
        return str->at(currentCh++);
    }

    unsigned int currentCh;
    unsigned int currentW;
    string * str;
    vector<string> words;
};

You can update internal states anyway you like, for example add:

char operator()(unsigned int index)
{
    currentCh = index;
    return this->operator()();
}

Then in your code somewhere you can do:

generator g;
g();  // get first character
g(2); // resets the index in this case ...
g();  // get next character (the logic is a bit off but it depends what you need)

Usage:

int main()
{
    generator g;
    for (unsigned int i = 30; --i; ) cout << g() << endl;
}

Output:

F
o
o
b
a
r
B
a
r
f
o
o
F
o
o
b
a
r
B
a
r
f
o
o
F
o
o
b
a
stefanB