You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于《Modern C++ Design》的模板类层级Object Factory注册问询

Got it, let's break down how to handle template class hierarchy registration for your Object Factory built from Modern C++ Design—I’ve spent plenty of time tinkering with these patterns, so let’s dive in.

First, let’s align on the core setup you’re working with: a singleton factory that maps identifiers to creator functions, returning pointers to your Base class. The tricky part with template classes is that every instantiation (like Derived<int> vs Derived<std::string>) is a distinct type, each needing its own factory registration entry.

Template Class Hierarchy Registration for Alexandrescu-Style Factories

1. Core Problem: Registering Template Specializations

For non-template derived classes, you’d typically use a static registration object (like the book’s RegisterInFactory helper) that hooks into the singleton factory on initialization. But template classes need a way to trigger this registration automatically for every unique specialization.

Solution: Template Registration Helpers

Adapt the book’s static registration pattern to work with templates by creating a templated helper struct. Here’s a simplified implementation:

// object_factory.h (simplified core)
class ObjectFactory {
public:
    using Creator = Base* (*)();
    
    void Register(const std::string& type_id, Creator creator) {
        creator_map_[type_id] = creator;
    }

    Base* Create(const std::string& type_id) {
        auto it = creator_map_.find(type_id);
        return it != creator_map_.end() ? it->second() : nullptr;
    }

private:
    std::unordered_map<std::string, Creator> creator_map_;
};

// Singleton wrapper (Meyers pattern to avoid init order issues)
template <typename T>
class Singleton {
public:
    static T& Instance() {
        static T instance;
        return instance;
    }
private:
    Singleton() = default;
    ~Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

using SingletonBaseFactory = Singleton<ObjectFactory>;

// Templated registration helper
template <typename Derived>
struct TemplateRegistrar {
    TemplateRegistrar(const std::string& type_id) {
        SingletonBaseFactory::Instance().Register(type_id, []() -> Base* {
            return new Derived(); // Replace with smart pointers in production!
        });
    }
};

Now, hook this helper into your template-derived class by declaring a static instance of it:

// Example template-derived class
template <typename T>
class Derived : public Base {
    // Static registrar to auto-register the specialization
    static const TemplateRegistrar<Derived<T>> registrar;
public:
    // ... your class implementation ...
};

// Define the registrar with a unique ID per specialization
template <typename T>
const TemplateRegistrar<Derived<T>> Derived<T>::registrar(
    "Derived<" + GetTypeName<T>() + ">"
);

// Helper for readable, portable type names (avoids compiler-specific typeid output)
template <typename T>
std::string GetTypeName() { return typeid(T).name(); }

// Specialize for common types to make IDs human-readable
template <>
std::string GetTypeName<int>() { return "int"; }

template <>
std::string GetTypeName<std::string>() { return "std::string"; }

2. Handling Deep Template Hierarchies

If you have nested template classes (e.g., template <typename T> class MidDerived : public Base, and template <typename T> class FinalDerived : public MidDerived<T>), the pattern stays identical. Each specialization of FinalDerived gets its own static registrar, which registers a creator function for that exact type.

The key rule here: every distinct instantiated type—even if it’s a template specialization—needs a unique entry in the factory’s creator map.

3. Avoiding Static Initialization Order Issues

A common pitfall with this pattern is the static initialization order fiasco, where your registrar tries to register with the factory before the factory singleton is initialized. Using the Meyers singleton pattern (local static instance in Instance()) fixes this, since the factory is initialized on first access—guaranteeing it exists when the registrar runs.

4. Alternative: Type-Safe Factory for Templates

If you don’t need to create objects from runtime strings (e.g., config files), you can make the factory type-safe by using type_info hashes instead of string IDs:

// Enhanced ObjectFactory with type-safe registration
class ObjectFactory {
public:
    template <typename Derived>
    void Register() {
        const auto type_hash = typeid(Derived).hash_code();
        creator_map_[type_hash] = []() -> Base* {
            return new Derived();
        };
    }

    template <typename Derived>
    Derived* Create() {
        const auto type_hash = typeid(Derived).hash_code();
        auto it = creator_map_.find(type_hash);
        return it != creator_map_.end() ? static_cast<Derived*>(it->second()) : nullptr;
    }

private:
    std::unordered_map<size_t, Creator> creator_map_;
};

// Updated registrar for type-safe setup
template <typename T>
const struct TypeSafeRegistrar {
    TypeSafeRegistrar() {
        SingletonBaseFactory::Instance().Register<Derived<T>>();
    }
} Derived<T>::registrar;

// Usage (no string IDs needed!)
Derived<int>* my_obj = SingletonBaseFactory::Instance().Create<Derived<int>>();

This approach eliminates manual ID management and reduces human error, though it gives up the ability to create objects from runtime string identifiers.


内容的提问来源于stack exchange,提问作者Davide

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:24:10