stl stack fun

Started by
11 comments, last by Zahlman 17 years, 7 months ago
So, if I'd create the stack in main(), out of App, and just want it to run its respective functions like App.Exit(), App.MainLoop(), and App.Menu(), how do I do it? I hit recursiveness wall. I'm sure there's some way to do that, but I've no idea how. :S
If only noobs were boobs... the world would be a better place.
Advertisement
I havn't had a chance to fully grok what the remaining problem is, so I'm instead going to attack the original problem from the angle I'd take.

It involves using the Boost C++ Libraries, a wonderful toolset that belongs in every C++ programmer's environment in my opinion.

Part of the problem, which I gather has already been touched on, is that member functions don't behave exactly like static functions - they're not interchangable. Part of the boost library helps solve exactly this problem, and is the route I'm going to take.

#include <boost/function.hpp>
typedef boost::function< void ( void ) > StateFunction;

Okay, this is pretty simple - we typedef StateFunction to a boost::function that has a signature of "void ( void )" (returns nothing, takes no arguments). Same basic deal as the function pointer typedef.

We can have these directly in the stack:

#include <boost/bind.hpp>#include <stack> //not sure this is the exact header :Sclass Application {    std::stack< StateFunction > stateStack;public:    Application();    void MainLoop();    void Exit();private: //states    void Menu();    void Game();};Application::Application() {    stateStack.push( boost::bind( & Application::Menu , this ) );    stateStack.push( boost::bind( & Application::Game , this ) );}void Application::MainLoop() {    while ( ! stateStack.empty() ) {        stateStack.top()();        //spread out into two steps:        //  const StateFunction & function = stateStack.top();        //  function();    }}...


boost::bind is a tool that lets us "bind" arguments that will always be used when calling this function. We used it above so that we didn't have to have an explicit "this" parameter in our boost::function and pass it every time - instead, it will always be the same as the Application they were constructed in.

Now, all these member functions still have their "this" data, so no recursion problems (if this is what you were refering to later on):

void Application::Exit() {    while ( ! stateStack.empty() ) stateStack.pop();}void Application::Menu() {    ...    if ( KeyDown( ESC ) ) Exit();    ...}


Hope this helps.
Quote:Original post by Bregma
class Function{public:  virtual void execute() const = 0;};


In C++, we normally spell "execute()" as "operator()()". :)

Quote:Original post by EliteWarriorManTis
So, if I'd create the stack in main(), out of App, and just want it to run its respective functions like App.Exit(), App.MainLoop(), and App.Menu(), how do I do it? I hit recursiveness wall. I'm sure there's some way to do that, but I've no idea how. :S


I can't understand that.

What is 'App'?

You say "run its respective functions" in a context that sounds like "its" refers to "the stack created in main()", but then say "App.whatever".

The idea is to have the stack be a *member of* some other object.

As for invoking functions that are contained on the stack, you were already given an example. All you need to do is get at the stack.

Looking back at the original code that you posted... what the REAL problem is, is this:

struct StateStruct{   typedef void (*StatePointer)();};void App::Exit() {cout << "I am exiting game." << endl;stateStack.pop();}StateStruct state;state.StatePointer = Exit; // <-- DevCPP throws out error here


The type of 'Exit' IS NOT void(*)(). It is void(App::*)(). It is a pointer to a member function, which is not really a pointer in the traditional sense. Which is why you are in a world of pain. The brief summary is: How do you expect to store the concept of "App::Exit()" somewhere and then invoke it as if it were a plain function, when it's a member function? Hint: which App does it get invoked on? Ok, how does the *compiler* know that?

With functors, you can eliminate the problem by having the object store a pointer back to the App instance, and then invoking the member function on that instance from the functor.

Example:

#include <iostream>#include <stack>struct Function {  virtual void operator()() const = 0;};class Exiter: public Function {  App* app;  public:  Exiter(App* app): app(app) {}  void operator()() const { app->Exit(); }};class App() {  std::stack<Function*> stateStack;  void cleanup() {    // we probably should get rid of *all* states, yeah?    while (!stateStack.empty()) {      delete stateStack.top();      stateStack.pop();    }  }  public:  // DO NOT write a member function like App::Init(). Use constructors to  // construct things. Notice your original example failed to call Init().  // With a constructor, it is impossible to make that error.  App() { stateStack.push(new Exiter(this)); }  ~App() { cleanup(); }  // Because we make sure of cleaning up in the destructor (RAII), we don't  // really need to do it in Exit(); we could instead set a flag that is  // checked in MainLoop(). But this way is pretty straightforward...  void MainLoop() {    while (!stateStack.empty()) {      // Grab a pointer to a function-object, dereference to get the function-      // object, and call it. The Exiter's operator() will dereference its      // App pointer (getting back this object!), and invoke the Exit() of that      // App instance (the current one, in our example, because we only made      // one). But now we have the flexibility e.g. to make several Apps, and      // have one of them tell another to Exit() via its states.      // Of course, maybe we don't *want* that, which is exactly why you got      // some advice not to make an App class :P      (*stateStack.top())();    }  }  void Exit() {    std::cout << "I am exiting game." << std::endl;    cleanup();  }};// By the way, there is absolutely no reason to allocate App dynamically.int main(int argc, char *argv[]) {  App().MainLoop();}

This topic is closed to new replies.

Advertisement