Skip to content

Instantly share code, notes, and snippets.

@jesyspa
Created October 13, 2012 15:23
Show Gist options
  • Save jesyspa/3884977 to your computer and use it in GitHub Desktop.
Save jesyspa/3884977 to your computer and use it in GitHub Desktop.
Context handler system
#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