C ++ Самостоятельная регистрация фабрики в библиотеке: в приложении нет зарегистрированных типов - PullRequest
1 голос
/ 17 января 2020

Я изменил фабричный шаблон с саморегистрацией типов из этого сообщения в блоге для своих собственных нужд. Вместо строк я использую UUID (используя boost.uuid) для регистрации и построения моих node подклассов.

factory и соответствующие подклассы node и node_* являются частью библиотеки. Я связываюсь с этой библиотекой (stati c library) в моем клиентском приложении и затем хочу использовать библиотечную функцию factory::build() для создания объектов на основе известных UUID. хорошо работает как в качестве отдельного минимального примера (см. ниже), так и в самой моей библиотеке. Однако, когда я использую функцию factory::build(), предоставляемую библиотекой в ​​моем приложении, я замечаю, что карта factory::m_generators пуста, и поэтому фабрика не знает, как создавать какие-либо объекты. Когда я сначала создаю фиктивный объект вручную, фабрика может создавать дополнительные экземпляры того же класса. Это указывает на то, что подклассы node никогда не создаются и, следовательно, никогда не получают возможности зарегистрироваться на фабрике.

Как я могу решить эту проблему?

Вот мой код ( живой пример ):

#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;
}


Этот мой предыдущий вопрос имеет отношение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...