如何为模板化类型提供 void* 访问器?

How to provide a void* accessor for a templated type?

本文关键字:void 访问 类型      更新时间:2023-10-16

我有一个模板化的自定义容器类:

template<typename T>
class MyContainer {
T Get();
void Put(T data);
};

我想将指向此容器的指针传递给一个函数,该函数将作为通用数据访问容器的数据 - 即char*void*.想想序列化。这个函数有点复杂,所以由于模板的原因,最好不要在标题中指定它。

// Errors of course, no template argument
void DoSomething(MyContainer *container);

我可以要求用户提供 lambda 或子类或执行转换的东西。但我似乎想不出一个干净的方法来做到这一点。

我考虑过通过MyContainer保存一些具有virtual void Serialize(void *dest) = 0;函数的抽象MyData类的容器来完全避免模板。用户将MyData子类以提供其类型和序列化,但这似乎变得非常复杂。效率也很低,因为它需要存储指向MyData的指针以避免对象切片,并且MyData通常非常小,并且容器将容纳大量(大量指针存储和取消引用)。

你不需要任何char*void*或继承。

请考虑以下简化的实现:

template <class T>
void Serialize (std::ostream& os, const MyContainer<T>& ct) {
os << ct.Get();
}

突然间,这适用于具有合适operator<<过载的任何T

没有合适的operator<<重载的用户类型怎么办?只需告诉用户提供一个。

当然,您可以使用任何重载函数。它不必命名为operator<<.您只需要将其名称和签名传达给用户并要求他们重载即可。

您可以使用纯虚函数为容器引入非模板基类,该函数返回指向原始数据的指针并在容器中实现它:

class IDataHolder
{
public:
virtual ~IDataHolder(); // or you can make destructor protected to forbid deleteing by pointer to base class
virtual const unsigned char* GetData() const = 0;
};
template<typename T>
class MyContainer : public IDataHolder
{
public:
T Get();
void Put(T data);
const unsigned char* GetData() const override { /* cast here internal data to pointer to byte */}
};
void Serialize(IDataHolder& container)
{
const auto* data = container.GetData();
// do the serialization
}
我想

将指向此容器的指针传递给一个函数,该函数将作为通用数据访问容器的数据 - 即char*void*.想想序列化。

一般做不到,因为你对T一无所知.通常,类型不能通过char *或类似方式作为原始 blob 进行处理(例如复制、访问等)。

因此,您需要限制T可以是什么,最好是强制执行它,否则永远不要将其用于会触发未定义行为的T。例如,您可能希望断言std::is_trivially_copyable_v<T>成立。尽管如此,在处理此类数据时,您仍然必须考虑其他可能的问题,例如字节序和打包。

这个函数有点复杂,所以由于模板的原因,最好不要在标题中指定它。

不知道你这是什么意思。编译器可以非常轻松地处理标头,特别是大量的模板代码。只要你没有达到一些 Boost 库的水平,你的编译时间就不会爆炸。

我考虑过通过MyContainer保存一些具有virtual void Serialize(void *dest) = 0;函数的抽象MyData类的容器来完全避免模板。用户将MyData子类以提供他们的类型和序列化,但这似乎变得非常复杂。效率也很低,因为它需要存储指向MyData的指针以避免对象切片,并且MyData通常非常小,并且容器将容纳大量(大量指针存储和取消引用)。

一般来说,如果你想要一个模板,做一个模板。为此使用动态调度可能会降低性能,特别是当您必须对即使是简单的类型进行调度时。


最后一点,我建议看看一些可用的序列化库,看看他们是如何实现的,不仅在性能方面,而且在易用性、与现有代码的集成等方面。例如,Boost Serialization和Google Protocol Buffers。