As I wrote in my previous blog post, I’m about to rewrite my Ruby Wallet project in C++17. See WalletCpp on my GitHub profile.
I want to use best practice and the latest standard C++17. So no raw pointers anymore. You should get rid of your raw pointers in your C++ code. You can do almost everything the same way using smart pointers.
Let’s assume you want to create a Command Factory to produce Command
objects from a given name. You need a base class that every command class will derivative from.
Let’s dive straight into the code.
class Command; // Base class
class AddCommand : public Command {};
class EditCommand : public Command {};
class DeleteCommand : public Command {};
In this example above we have 3 commands: Add, Edit and Delete.
We will be able to get a new Command
object for a specific name. For the purpose of this post I assume that the header file is already guarded.
// command_factory.hpp
// already guarded
#include <string> // for std::string
#include <map> // for std::map
#include <functional> // for std::function
class CommandFactory final
{
public:
static void setup() noexcept;
std::unique_ptr<Command> makeCommand(const std::string&) const;
private:
static bool isSetup;
static std::map<std::string, std::function<std::unique_ptr<Command>()>> creators;
};
};
Here the corresponding implementation file:
// command_factory.cpp
void CommandFactory::setup() noexcept
{
if (isSetup) {
// Run setup only once.
return;
}
isSetup = true;
// Clear creators.
creators.clear();
creators["add"] = []()->std::unique_ptr<Command> {
return std::make_unique<AddCommand>();
};
creators["edit"] = []()->std::unique_ptr<Command> {
return std::make_unique<EditCommand>();
};
creators["delete"] = []()->std::unique_ptr<Command> {
return std::make_unique<DeleteCommand>();
};
}
std::unique_ptr<Command> CommandFactory::makeCommand(const std::string& _name) const
{
auto fn = creators[_name];
if (fn == nullptr) {
throw (std::string{"Command not found: "} + _name);
}
return fn(); // Call lambda and return new unique pointer.
}
// Private
bool CommandFactory::isSetup = false;
std::map<std::string, std::function<std::unique_ptr<Command>()>>
CommandFactory::creators;
The setup()
member function will only add lambda functions to a <string,function>
map. At this point we do not create new objects. Only lambda function. New objects will be created when makeCommand()
is called.
You can now we use it. First, setup the factory:
CommandFactory::setup();
CommandFactory factory;
Then new commands can be created using a name:
auto addCommand = factory.makeCommand(std::string{"add"});
auto editCommand = factory.makeCommand(std::string{"edit"});
In this example we use a fixed string to create a new command. A more realistic example:
auto command = factory.makeCommand(commandName);
Where commandName
has a dynamic value and you do not know which object command
holds. See how I used a Factory Class in my WalletCpp project.