纯虚拟成员函数必须处理模板化的缓冲区-如何处理

Pure virtual member function has to process a templated buffer - how to?

本文关键字:处理 缓冲区 何处理 函数 虚拟成员      更新时间:2023-10-16

我有一个基类PixelBuffer,它有一个虚拟函数,可以将Pixel Buffer的内容复制到模板化的Buffer对象中。PixelBuffer可能有几个继承的实现,如OpenGLPixelBuffer/DXPixelBuffer,它们实现纯虚拟复制到缓冲区函数(伪代码):

class PixelBuffer
{
public:
    virtual void copyToBuffer(? buffer) = 0; // how to declare this?
};

PixelBuffer有一个内部数据类型,可以是不同的基本类型(int、uint、float、char…)

更重要的是我要复制到的缓冲区的数据类型。缓冲区类(可能)看起来像这样:

template <typename T>
class Buffer
{
public:
    Buffer() : m_data(), m_size(0) {}
    Buffer(std::size_t buffsize) : m_data(new std::vector<T>(buffsize)), m_size(buffsize) { }
    std::shared_ptr<std::vector<T>> data() { return m_data; }
    std::size_t size() { return m_size; }
    void allocate(std::size_t buffsize) { 
        m_data = std::shared_ptr<std::vector<T>>(new std::vector<T>(buffsize)); 
        m_size = buffsize; 
    }
private:
    std::shared_ptr<std::vector<T>> m_data;
    std::size_t m_size;
};
typedef Buffer<float> FloatBuffer;
typedef Buffer<char> ByteBuffer;

我正在寻找一种方法来做这样的事情:

PixelBuffer* pbObj = ...;
FloatBuffer dest;
pbObj->copyToBuffer(dest); // does allocation and copying

由于不可能对PixelBuffer成员函数进行模板化,我现在不知道如何解决这个问题。我知道我需要很多实现来复制不同的缓冲区数据类型,但我不知道在哪里实现它们,也不知道如何在不向PixelBuffer(针对每种类型的缓冲区)添加一堆函数的情况下实现它们。

也许这可以使用访问者模式或策略来完成?

编辑:回答评论:是的,应该可以从任何PixelBuffer类型复制到任何Buffer类型。但是,应该只支持那些实现复制的类型!

我仍然不确定你需要什么,但如果你想通过Buffer的各种专业化,那么你需要独特的方法,而不是单一的虚拟方法:

virtual void copyToBuffer(Buffer<char> & buffer) = 0;
virtual void copyToBuffer(Buffer<float> & buffer) = 0;

然后,您可能会在子类中重写这些,并可能在不支持复制操作的任何情况下抛出异常。你的问题表明你不想这样做,尽管我觉得如果你需要处理的缓冲专业有限,这是最好的解决方案。

如果你只想要一个方法,你需要将Buffer的每个专业化所共有的功能抽象成一个每个专业化扩展的类,并引用它:

virtual void copyToBuffer(BufferBase & buffer) = 0;
template <typename T>
class Buffer : public BufferBase
{
    // ...

在这种情况下,copyToBuffer方法的实现将仅限于BufferBase中提供的功能,所以我怀疑这个解决方案是否对您可行。

另一方面,如果你只想有一个接受特定类型缓冲区的方法,并且每个子类的类型不同,那么这个方法根本不应该在基类中声明——你应该在每个子类中用合适的参数类型来声明这个方法。在这种情况下,最干净的解决方案可能是根据PixelBuffer类所支持的缓冲区的元素类型对其进行参数化:

template <typename E>
class PixelBuffer : public PixelBufferBase
{
    public:
    virtual void copyToBuffer(Buffer<E> &) = 0;
}

你的例子变成:

PixelBufferBase* pbObj = ...;
PixelBuffer<float> *fpbObj = dynamic_cast<PixelBuffer<float> *>(pbObj);
FloatBuffer dest;
fpbObj->copyToBuffer(dest); // does allocation and copying

(出于性能考虑,您可能会使用static_cast,但要牺牲一定的安全性)。

您实际上并不需要基于策略的设计;只有当copyToBuffer实现可以在不同的PixelBuffer子类之间共享(这听起来不太可能),并且在任何情况下您仍然需要参数化PixelBuffer类型时,您才能从策略中获得好处。

我也不相信访问者模式能帮助你解决这个问题——它需要多个函数来处理多个PixelBuffer/Buffer类型;您可以将重载的方法转移到另一个类,但也不能避免对它的需要。

我也遇到过同样的问题。我使用void *作为类型,并将指针转换为实现中正确的Buffer。它不漂亮,但它很有效,对表演没有任何影响。

但是,如果您的PixelBuffer将复制到多个类型的Buffer,而只有一个copyToBuffer函数。您必须向不同类型的缓冲区添加另一个参数:

enum class BufferType
{
    INT,
    CHAR,
    FLOAT,
    //...
};

class PixelBuffer
{
public:
    //....
    virtual void copyToBuffer(void* buffer, BufferType buffertype) = 0;
}
virtual void DXPixelBuffer::copyToBuffer(void* buffer, BufferType buffertype)
{
    switch(buffertype){
        case BufferType::FLOAT:
            Buffer<float>* pbuffer = static_cast<Buffer<float>*>(buffer);
            //...
            break;
            //...

您可以使用traits类和wrap函数使对copyToBuffer的调用更加安全。

template<typename T> struct BufferTraits;
template<> struct BufferTraits<float>{
    static constexpr BufferType Type = BufferType::FLOAT;
};
template<> struct BufferTraits<char> {
    static constexpr BufferType Type = BufferType::CHAR;
};
class PixelBuffer
{
public:
    template<typename T> void PixelBuffer::copyToBuffer(Buffer<T>& buffer){this->copyToBuffer(reinterpret_cast<void *>(&buffer), BufferTraits<T>::Type);}
//...
}

您的示例将保持不变:)

PixelBuffer* pbObj = ...;
FloatBuffer dest;
pbObj->copyToBuffer(dest);