Created
October 13, 2012 15:23
-
-
Save jesyspa/3884977 to your computer and use it in GitHub Desktop.
Context handler system
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <memory> | |
#include <stack> | |
#include <vector> | |
#include <iostream> | |
#include <functional> | |
// Stores an overview of everything. It is very easy to get this wrong; | |
// try to keep as few things as possible directly in this class, and as | |
// much as possible in more focused classes. | |
class World; | |
// Sometimes you'll want things to operate on the world. I prefer returning | |
// the operation that must be performed over passing the world around | |
// everywhere. This is a type that stores an "operation on the world". | |
typedef std::function<void(World&)> WorldUpdate; | |
// I'm a sucker for hungarian notation for interfaces. | |
class IContext { | |
// Make sure IContexts can't be copied or assigned. This'll save you | |
// some headaches debugging splicing errors. | |
IContext(IContext const&) = delete; | |
IContext& operator=(IContext const&) = delete; | |
// This is called the template method pattern. You don't strictly | |
// have to use it here, but it's a way of making sure you know where | |
// virtual functions are called from. This makes it easier to check | |
// preconditions and postconditions. | |
virtual WorldUpdate do_process(std::string const& line) = 0; | |
public: | |
IContext() = default; | |
virtual ~IContext() = default; | |
WorldUpdate process(std::string const& line) { | |
return do_process(line); | |
} | |
}; | |
// You'll usually be working with shared pointers to contexts, so give | |
// them a proper name. We could also use unique pointers, but I can't | |
// be bothered to figure out how exactly to get those to work with bind | |
// (see later). | |
typedef std::shared_ptr<IContext> ContextSPtr; | |
class World { | |
World(World const&) = delete; | |
World& operator=(World const&) = delete; | |
std::stack<ContextSPtr> contexts_; | |
public: | |
World(); | |
void process(std::string const& line) { | |
if (contexts_.empty()) { | |
std::cout << "Error: no context!\n"; | |
return; | |
} | |
auto update = contexts_.top()->process(line); | |
update(*this); | |
} | |
static void do_nothing(World&) {} | |
static void pop_context(World& w) { | |
w.contexts_.pop(); | |
} | |
static void push_context(World& w, ContextSPtr&& ptr) { | |
w.contexts_.push(std::move(ptr)); | |
} | |
}; | |
class BaseContext : public IContext { | |
WorldUpdate do_process(std::string const& line); | |
}; | |
class EchoContext : public IContext { | |
WorldUpdate do_process(std::string const& line); | |
}; | |
class IgnoreContext : public IContext { | |
WorldUpdate do_process(std::string const& line); | |
}; | |
WorldUpdate BaseContext::do_process(std::string const& line) { | |
if (line == "echo") { | |
std::cout << "Going into echo mode. Use 'end' to stop.\n"; | |
return std::bind(World::push_context, std::placeholders::_1, std::make_shared<EchoContext>()); | |
} | |
if (line == "ignore") { | |
std::cout << "Going into ignore mode. Use 'end' to stop.\n"; | |
return std::bind(World::push_context, std::placeholders::_1, std::make_shared<IgnoreContext>()); | |
} | |
if (line.empty() || line == "help") { | |
std::cout << "Available contexts:\n"; | |
std::cout << " echo -- print all incoming lines\n"; | |
std::cout << " ignore -- do nothing with incoming lines\n"; | |
} else { | |
std::cout << "Unknown command: " << line << '\n'; | |
} | |
return World::do_nothing; | |
} | |
WorldUpdate EchoContext::do_process(std::string const& line) { | |
if (line == "end") | |
return World::pop_context; | |
std::cout << line << '\n'; | |
return World::do_nothing; | |
} | |
WorldUpdate IgnoreContext::do_process(std::string const& line) { | |
if (line == "end") | |
return World::pop_context; | |
return World::do_nothing; | |
} | |
World::World() { | |
contexts_.push(std::make_shared<BaseContext>()); | |
} | |
int main() { | |
World world; | |
std::string line; | |
while (std::getline(std::cin, line)) | |
world.process(line); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment