C++ Self registering factory in library: No types registered in application

回眸只為那壹抹淺笑 提交于 2020-01-23 17:27:49

问题


I modified the factory pattern with self registering types from this blog post for my own needs. Instead of strings I use UUIDs (using boost.uuid) to register & build my node sub-classes.

The factory and corresponding node and node_* subclasses are part of a library. I link against that library (static library) in my client application and then want to use the library's factory::build() function to build objects based on the known UUIDs.

The problem I am facing is that everything is working well both as a standalone minimal example (see below) and within my library itself. However, when I use the factory::build() function provided by the library in my application I notice that the factory::m_generators map is empty and therefore the factory doesn't know how to build any objects. When I manually create a dummy object first the factory is able to build further instances of the same class. This indicates that the node sub-classes never get instantiated and therefore also never get a chance to register themselves at the factory.

How can I solve this problem?

Here's my code (live example):

#include <iostream>
#include <unordered_map>
#include <functional>
#include <memory>
#include <boost/functional/hash.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/string_generator.hpp>

class node;

/**
 * @brief The factory class to build @p node items.
 */
class factory
{
public:
    using key_type  = boost::uuids::uuid;
    using key_hash  = boost::hash<key_type>;
    using generator = std::function<std::unique_ptr<node>()>;

    template<typename Derived>
    struct registrar
    {
        registrar(const key_type& key)
        {
            factory::instance().register_generator(key, [](){
                return std::make_unique<Derived>();
            });
        }

        registrar(const std::string& uuid_string)
        {
            try {
                boost::uuids::string_generator gen;
                registrar(gen(uuid_string));
            } catch (...) {
                ;
            }
        }
    };

    static factory& instance() noexcept
    {
        static factory f;
        return f;
    }

    bool register_generator(const key_type& key, generator&& generator)
    {
        auto [it, emplaced] = m_generators.try_emplace(key, std::move(generator));
        return emplaced;
    }

    [[nodiscard]] std::unique_ptr<node> build(const key_type& key) const
    {
        if (const auto& it = m_generators.find(key); it not_eq m_generators.cend())
            return it->second();
        return nullptr;
    }

    [[nodiscard]] std::unique_ptr<node> build(const char* uuid_string) const noexcept
    {
        try {
            boost::uuids::string_generator gen;
            return build(gen(uuid_string));
        } catch (...) {
            return nullptr;
        }
    }

private:
    std::unordered_map<key_type, generator, key_hash> m_generators;

    factory() = default;
    factory(const factory& other) = default;
    factory(factory&& other) = default;
    virtual ~factory() = default;
};


/**
 * @brief The node base class.
 */
struct node
{
    node(const std::string& uuid_string) :
        m_uuid_string(uuid_string)
    {
    }

    [[nodiscard]] const std::string& uuid_string() const noexcept {
        return m_uuid_string;
    }

private:
    std::string m_uuid_string;
};

/**
 * @brief A template for @p node subclasses.
 */
template <class derived>
struct node_template :
    node,
    factory::registrar<derived>
{
    node_template(const std::string& uuid_string) :
        node(uuid_string),
        factory::registrar<derived>(uuid_string)
    {
        (void) registered;
    }

    static bool do_register() {
        std::cout << "node_template::do_register()" << std::endl;
        derived d; // I am not sure if one should in some way force this to not be optimized away.
        return true;
    }

    inline static bool registered = do_register();
};

struct A : node_template<A> {
    A() : node_template("63cb8eeb-b90b-46c7-aaa8-3a349fcba3c5") { }
};

struct B : node_template<B> {
    B() : node_template("1f24abfc-936f-4524-ae3b-cc346335ecbb") { }
};

static void build_and_print(const std::string& uuid_string)
{
    if (auto node = factory::instance().build(uuid_string.c_str()); node)
        std::cout << "node.uuid_string() = " << node->uuid_string() << std::endl;
    else
        std::cout << "Cannot build node object: Unknown UUID." << std::endl;
}

int main(void)
{
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    /// PROBLEM: If I do not construct these objects, they never register themselves at the factory. ///
    ////////////////////////////////////////////////////////////////////////////////////////////////////
#if 0
    A a;
    B b;
#endif

    // A
    build_and_print("63cb8eeb-b90b-46c7-aaa8-3a349fcba3c5");

    // B
    build_and_print("1f24abfc-936f-4524-ae3b-cc346335ecbb");

    // Unknown UUID
    build_and_print("9b20cc29-c7ca-4796-acb2-6ca6b80fa934");

    return 0;
}


This previous question of mine is related.

来源:https://stackoverflow.com/questions/59793556/c-self-registering-factory-in-library-no-types-registered-in-application

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!