Step 2 of 7
A factory registry
A factory creates objects by name: the name comes from a config file, a network message or a command line, and the result is returned through a base-class pointer. The naive version is a giant if/else chain that must be edited for every new type. Large codebases use a registry instead: a map from name to a creator function. Adding a type means registering one more entry, and the factory itself never changes.
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <string>
struct Codec {
virtual ~Codec() = default;
virtual std::string encode(const std::string& s) const = 0;
};
struct Upper : Codec {
std::string encode(const std::string& s) const override {
std::string r = s;
for (char& c : r) if (c >= 'a' && c <= 'z') c = char(c - 32);
return r;
}
};
struct Reverse : Codec {
std::string encode(const std::string& s) const override { return {s.rbegin(), s.rend()}; }
};
int main() {
std::map<std::string, std::function<std::unique_ptr<Codec>()>> registry;
registry["upper"] = [] { return std::make_unique<Upper>(); };
registry["reverse"] = [] { return std::make_unique<Reverse>(); };
for (const char* name : {"reverse", "rot13", "upper"}) {
auto it = registry.find(name);
if (it == registry.end()) {
std::cout << name << ": unknown codec\n";
continue;
}
std::unique_ptr<Codec> c = it->second(); // call the creator
std::cout << name << ": " << c->encode("hello") << "\n";
}
}
reverse: olleh
rot13: unknown codec
upper: HELLO
How it works
- Each map value is a function that takes nothing and returns a
std::unique_ptr<Codec>. A capture-less lambda[] { return std::make_unique<Upper>(); }fits, becauseunique_ptr<Upper>converts tounique_ptr<Codec>. it->second()calls the stored creator and gets a brand new object. The caller owns it, which theunique_ptrreturn type makes obvious.- Unknown names are looked up with
find, never[], which would insert an empty function.
Your task
add: ifcreators_already contains the name, returnfalse; otherwise store it (move thestd::functionin) and returntrue.create:findthe name; returnnullptrif missing, otherwise call the creator.names: astd::mapis already sorted by key, so loop over it and collect the keys.
Your turn: implement Factory::add (returns false if the name is already registered), Factory::create (returns nullptr for unknown names) and Factory::names (sorted).
Previous: Strategies as closures Next: Observers with weak_ptr