c++ - 下面描述的概念如何在 C++14 中设计?

标签 c++ design-patterns visual-c++ c++14

我必须为自动化引擎开发一个 C++ 框架,它能够连续运行任意用户操作(具有抽象方法 Execute() 的 C++ 类)。除了通常的输入和输出参数外,这些 Action 还可以访问一个通用的数据结构,该结构用作所有 Action 的全局环境或上下文。我的问题是关于这个数据结构。

上下文扮演一个公共(public)存储的角色,任何 Action 都可以将一段数据放入其中,稍后其他一些 Action 可以获取并使用这些数据。因此,这个上下文应该表现为不同类型的命名元素的容器,在设计时是未知的。元素可以是简单的整数、字符串或指向类的指针。它们的确切类型在运行之前是未知的。当然,当上下文被销毁时,它应该销毁它的所有元素。

有关 std::any 的建议很有用,但不能解决问题。 std::any 提供了一种在内部存储数据的便捷方式,但我不希望 std::any 在接口(interface)中。也就是说,我希望一个操作可以通过调用将其数据放入存储中

MyType* pObj1 = new MyType(...);    // MyType is the user's class
pContext->addData("UniqueName", pObj1);

稍后的其他操作可以通过调用来访问这些数据
MyType* pObj1 = pContext->getData("UniqueName");

有没有办法在现代 C++ 中实现这样的概念?

最佳答案

所以,在游戏开发中,这个概念通常被称为“黑板”(我确信其他域使用相同的模式——可能使用不同的名称)。

本质上,这只是一个简单的键值映射。

很多游戏做的就是拥有template <typename... Types> class Blackboard .黑板内部存储了一组 map ,每种类型一个,将存储在黑板的给定模板实例中。这样,库的作者并不知道所有可能的存储类,但用户知道并使用他们需要的所有类型定义了黑板,并且您避免了对支持类型的动态分配的需要。

根据您的问题,我假设您不想要这样的实现,并且想要保留一个将存储所有可能类型的黑板实例。您还希望在销毁黑板实例时销毁值。

我选择了一个通用的黑板实现,即只能移动,并且可以存储 unique_ptr .为此,我对轮子进行了一些重新发明,并制作了 any 的简化版本。 (在我的实现中称为 Handle),可用于仅可移动类型( std::any 不能存储 unique_ptr,因为它要求存储的类型可以复制构造)。 注意!这是一个幼稚和简单的实现,可以进行许多优化

这意味着黑板本身是不负责释放任何内存 ,但用户可以提供 unique_ptr (甚至 shared_ptr )如果他们希望黑板拥有变量。

C++ 黑板代码

class Blackboard
{
private:
    struct IHandle
    {
        virtual ~IHandle() = default;
    };
    template <typename T>
    struct Handle : public IHandle
    {
        Handle(T data)
            : m_Data(std::move(data))
        {
        }
        T m_Data;
        T* get()
        {
            return &m_Data;
        }
    };

public:
    template<typename T>
    void AddData(const std::string& key, T object)
    {
        m_Map[key] = std::make_unique<Handle<T>>(Handle<T>(std::move(object)));
    }

    template<typename T>
    T* GetData(const std::string& key)
    {
        auto it = m_Map.find(key);
        if (it != m_Map.end())
        {
            if (auto* handle = dynamic_cast<Handle<T>*>(it->second.get()))
            {
                return handle->get();
            }
        }
    return nullptr;
    }
private:
    std::map<std::string, std::unique_ptr<IHandle>> m_Map;
};

例子
struct MyType
{
    ~MyType() { std::cout << "~MyType()"; }
};
int main()
{
    Blackboard b;
    b.AddData("someVar", 7);
    int* someVar = b.GetData<int>("someVar");
    std::cout << "*someVar as int: " << *someVar << std::endl;
    *someVar = 88; 

    b.AddData("otherVar", std::make_unique<int>(99));
    std::cout << "*someVar after modifying, as int: " <<*(b.GetData<int>("someVar")) << std::endl;

    std::unique_ptr<int>* otherVar = b.GetData<std::unique_ptr<int>>("otherVar");
    std::cout << "*otherVar->get() as unique_ptr<int>: " << *(otherVar->get()) << std::endl;

    std::cout << "otherVar as int: " << b.GetData<int>("otherVar") << std::endl;
    //Blackboard blackboardCopy{ b }; //Does not compile
    Blackboard movedBB{ std::move(b) }; //compiles fine, b now does not contain anything
    movedBB.AddData("MyTypeVar", std::make_unique<MyType>());

    //Because "MyTypeVar" is a unique_ptr<MyType>, as soon as movedBB goes out of scope
    //MyType's dtor will be called
}

在我的电脑上输出:

*someVar as int: 7

*someVar after modifying through int*, as int: 88

*otherVar->get() as unique_ptr: 99

otherVar as int: 00000000

~MyType()



拥有内存

如果你想让黑板拥有自己的内存,可以很容易地制作一些模板魔术来将指针类型包装在 unique_ptr 中。在存储之前,返回 unique_ptr.get()getData()叫做。出于设计原因,我个人不喜欢这种方法,因为它使 Blackboard 承担了不止一项责任。

使用 std::any
你可以换掉我可怜的Handle std::any 的实现.为此,而不是存储unique_ptr<IHandle>在 map 内,只需存储 std::any .同时交换 dynamic_cast对于 std::any_cast .这样做的唯一缺点是您不能再将仅移动类型存储在黑板上,但另一方面您可以自由复制黑板。您还可以获得标准库为 std::any 所做的所有优化。 .

关于c++ - 下面描述的概念如何在 C++14 中设计?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59552259/

相关文章:

c++ - SHGetValue 查询 UAC 值时返回 2

c++ - 批量构建在 Visual Studio 2017 中不执行任何操作

C++:应用复合模式

javascript - 什么是 "Enterprise JavaScript"?

windows - 使用 Microsoft VC (cl.exe) 2010 (10.0) Express 和 ActivePerl 5.12.4/32 构建 Perl/C 模块

c++ - 它用什么样的二叉搜索树来实现 `std::set` ?

iOS/AVFoundation : Design pattern for asynch handlers when turning arrays of images into tracks and then into a single video?

c++ - C/C++运行时从何而来?

c++ - 从控制台读取 : operator>> for enum inside template class

c++ - 奇怪的类型转换行为