Skip to content

Instantly share code, notes, and snippets.

@komiga
Last active December 24, 2015 10:59
Show Gist options
  • Select an option

  • Save komiga/6788395 to your computer and use it in GitHub Desktop.

Select an option

Save komiga/6788395 to your computer and use it in GitHub Desktop.
More thorough example.
#ifndef EG_CMD_HPP_
#define EG_CMD_HPP_
#include <cstdint>
#include <utility>
#include <memory>
namespace Cmd {
// In my case there are many commands, so I am reducing the number of
// extra declarations & definitions I have to express by using a
// stub class with the declarations. This way I only have to specify
// the /definitions/ per command type, not the declarations.
// I also use macros to avoid having to modify the function prototypes
// if the base ever changes, but I've left those out for brevity.
using ID = std::uint32_t;
// Command types
enum class Type : unsigned {
PrintSomething
};
// Command stages; won't get into this, but there are multiple stages
// for most commands.
enum class StageType : unsigned {
Statement
};
class Stage;
using StageUPtr = std::unique_ptr<Cmd::Stage>;
// Interface for a command stage
class Stage {
public:
// There is type_info for the entire command which contains
// things such as stage instantiation (a function reference), but
// I've again elided it for brevity. This also contains
// serialization descriptors.
struct type_info final {
Cmd::StageType const type;
};
private:
Cmd::ID m_id;
// Implementation
protected:
virtual Cmd::Stage::type_info const&
get_stage_type_info_impl() const noexcept = 0;
virtual void execute_impl(Cmd::Stage&) = 0;
public:
virtual ~Stage() = 0;
Stage() = default;
// Properties
Cmd::Stage::type_info const&
get_stage_type_info() const noexcept {
return get_stage_type_info_impl();
}
Cmd::ID get_id() const noexcept {
return m_id;
}
void set_id(Cmd::ID const id) noexcept {
m_id = id;
}
// Operations
// Normally handle preconditions here
void execute(Cmd::Stage& initiator) {
execute_impl(initiator);
}
};
inline Stage::~Stage() = default;
// Stub for a command implementation
template<
Cmd::Type command_type,
Cmd::StageType stage_type,
class Data
>
class StageImpl final : public Stage {
private:
Data m_data;
Cmd::Stage::type_info const&
get_stage_type_info_impl() const noexcept override;
void execute_impl(Cmd::Stage&) override;
public:
~StageImpl() override = default;
StageImpl() = delete;
StageImpl(Data&& data)
: m_data(std::move(data))
{}
};
} // namespace Cmd
#endif // EG_CMD_HPP_
#include "Cmd.hpp"
#include "CmdPrintSomething.hpp"
#include <iostream>
namespace Cmd {
// This is normally handled through a macro.
// Because all of the stage classes are only local to the
// implementation file for a command, I simply name them after the
// stage type.
struct Statement final {
int x;
using impl = ::Cmd::StageImpl<
Cmd::Type::PrintSomething,
Cmd::StageType::Statement,
Statement
>;
};
// I actually expose type_info to another system to implement a
// constant-time type lookup table, so I ensure the name is unique to
// the command and stage.
static Cmd::Stage::type_info const
s_type_info_PrintSomething_Statement{
Cmd::StageType::Statement
};
// C++ requires the definitions of a class to be defined in the
// class' namespace.
// I don't want the actual definitions for a stage to be in that
// namespace because it pollutes the interface namespace (Cmd) and
// requires group definitions to be separated from the command
// definition.
// My argument with namespace deduction here is that a compiler
// should be able to understand (and in fact they do) that the
// definition pertains to a very specific class (outside of the
// namespace of definition) and should be able to compile it without
// fuss.
// E.g., if the enclosing namespace were Print (as it should be)
// instead of Cmd, this alias still points specifically to
// Cmd::StageImpl, not any other StageImpl.
// My inquiry is thus: why and where is this not permitted in the
// standard? The full namespace of the class is known. Both Clang and
// GCC understand the situation perfectly, too, judging by their error
// messages.
// The prototypes here are also handled through macros.
template<>
Cmd::Stage::type_info const&
Statement::impl::get_stage_type_info_impl() const noexcept {
return s_type_info_PrintSomething_Statement;
}
// It is very nice to have direct access to m_data here.
// With Daryle Walker's suggestion, it is not as direct.
template<>
void Statement::impl::execute_impl(Cmd::Stage&) {
std::cout
<< "group Print, command Something, stage Statement\n"
<< "x = " << m_data.x << '\n'
;
}
// Here are the actual public definitions for the Print command group.
// Luckily my structure has command groups under Cmd. If this were not
// the case, I would have to break out of Cmd, open Print, and refer
// to the stage classes through Cmd.
// Furthermore, if I wanted the stage classes to be outside of Cmd,
// I'd have to break out twice:
// namespace Print { declarations }
// namespace Cmd { implementation }
// namespace Print { public interface }
namespace Print {
Cmd::StageUPtr make_something(int const x) {
return Cmd::StageUPtr{new Statement::impl({
x,
})};
}
} // namespace Print
} // namespace Cmd
#ifndef EG_CMD_PRINT_SOMETHING_HPP_
#define EG_CMD_PRINT_SOMETHING_HPP_
#include "Cmd.hpp"
namespace Cmd {
namespace Print {
// This contains all of the command initiators for a group
// (and potentially other things).
// Used to create an initiator stage for the PrintSomething command.
Cmd::StageUPtr make_something(int const x);
} // namespace Print
} // namespace Cmd
#endif // EG_CMD_PRINT_SOMETHING_HPP_
#include "Cmd.hpp"
#include "CmdPrintSomething.hpp"
int main() {
Cmd::StageUPtr s{Cmd::Print::make_something(42)};
// Command initiator is always the first stage
s->execute(*s.get());
return 0;
}
@komiga

komiga commented Oct 2, 2013

Copy link
Copy Markdown
Author

GitHub screwed up the order. Thanks, GitHub.

Compiling with:

$ clang -std=c++11 -stdlib=libc++ -lc++ -pedantic -Wall -Wextra -g -o main main.cpp CmdPrintSomething.cpp

## or
$ g++ -std=c++11 -pedantic -Wall -Wextra -g -o main main.cpp CmdPrintSomething.cpp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment