gpt4 book ai didi

c++ - C++库和自注册类:客户端应用程序中的工厂映射为空

转载 作者:行者123 更新时间:2023-12-02 10:21:57 25 4
gpt4 key购买 nike

假设有一个C++库(我们将其称为lib)作为静态库包含在应用程序中(我们将其称为app)。在lib中,有一个基类node。节点的每个子类都由UUID标识。
我采用自我注册模式,以确保新类在工厂进行注册。工厂允许基于提供的UUID构建node子类对象。 app通过libfactory::build()函数构建对象。

我的工厂基于this brilliant blog post的代码。

我已将此代码改编为使用UUID(使用boost.uuid)而不是字符串,因为所有创建的类无论如何都需要由它们分配的UUID(出于外部依赖的原因)。

我遇到的问题是,如果我不“手动”创建每个node_template子类(即AB)的对象实例,则factory::m_generators映射为空。
当然,这是由于各种node -subclasss从未实例化,因此也从未注册自己。

这是我可运行的最小示例(live demo at coliru):

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

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

只要我在第136和137行中保留对象实例化,就可以通过 factory构建更多对象。但是一旦删除,(例如,将第135行更改为 #if 0),工厂生成器映射将为空。

我认为我理解问题所在,因为永远不会注册类,因为永远不会构造对象。但是,我不确定如何解决该问题。

当前 lib中有一个非常丑陋的头文件,该文件包含在 app中。 header 创建每个类的虚拟对象。当然,这不仅好,而且也有损于自学类(class)的全部目的。

我在这里想念什么?

最佳答案

在博客文章中,您缺少静态成员registered(也称为“// The really fun part”)。在基类中具有并实例化这样的静态变量会强制在所有派生类中实例化此静态变量,这会将该类注册为副作用。

编辑:博客文章中还有另外一个非常小但非常重要的代码:

    Registrar() : Base(Key{}) { (void)registered; }

这将确保使用 registered。因为 static变量仅在首次使用时实例化,否则不会调用该函数。

在您的情况下,将以下内容添加到 node_template应该可以工作:

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() {
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();
};

关于c++ - C++库和自注册类:客户端应用程序中的工厂映射为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59779679/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com