Christian Mayer's Weblog

C++ Factory Class

As I wrote in my previous blog post, I’m about to re-write 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> getCommand(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::getCommand(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 getCommand() 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.getCommand(std::string{"add"});
auto editCommand = factory.getCommand(std::string{"edit"});

In this example we use a fixed string to create a new command. A more realistic example:

auto command = factory.getCommand(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.

Posted on .
Categories: Programming
Tags: C++, Factory, Smart Pointers, Raw Pointers, Lambda

Categories | RSS Feed | Usage | Imprint
Copyright © 2006 by