如何表示一组任意长度和类型组合的值?

时间:2013-03-19 18:21:50

标签: c++ data-structures

我正在编写代码来分析n维数据集。我写了一个代表任意数据集轴的类。每个轴都可以有不同的数据类型,因此我选择使用模板编程。

class BaseAxis
{
};

template <class T>
class Axis : public BaseAxis
{
public:
    Axis(const T &begin, const T &end);

    const T begin;
    const T end;
};

这允许我创建任意类型的新Axis对象。用户可以通过添加正确的轴来配置更高级别的NTree,它代表整个数据集,如下所示:

ntree = new ntree::NTree<float>();
ntree->AddAxis<int>(0, 8);
ntree->AddAxis<float>(-100.0f, 200.0f);

这将创建一个二维浮点数据集,其整数轴从0到8,浮点轴从-100.0f到200.0f。要从数据集中获取值,您应该能够按如下方式查询数据结构:

ntree->Query(2, 2.5f);

我正在尝试弄清楚如何编写这样的函数(或可以表示这些参数的对象),因为它们可以是任意数量和类型。我已经阅读了关于boost :: variant但我不确定如何在这种情况下应用这个,因为我在c ++模板和库中没有足够的经验。

这是NTree类的相关代码部分。

template <class T>
class NTree
{
public:
    NTree();
    ~NTree();

    template <class A>
    void AddAxis(const A &start, const A &end);

protected:
    std::vector<BaseAxis*> *axes;
};

template <class T>
template <class A>
void NTree<T>::AddAxis(const A &start, const A &end)
{
    BaseAxis* newAxis = new Axis<A>(start, end);
    this->axes->push_back(newAxis);
}

因此我的问题如下:如何表示一组任意长度和类型组合的值?

1 个答案:

答案 0 :(得分:1)

您可以使用Typelistvariadic templates定义容器。这样,您可以在不使用不透明容器的情况下保持强类型安全。您可以从容器中添加远程轴,可能以复制缓冲区为代价。

一个干净的实现可以使用每种类型的专用分配器和代理类来聚合它们。然后,您可以在代理类中使用模板化访问器,该访问器将使用参数重新调用分配器并调用其访问函数。

这是N维容器的(非常截断的)实现。在这种情况下,卷由相同维度但不同类型的数组列表定义,因此不是所需要的,而是关闭:

 // opaque base class for allocator
 class UnsafeAllocator{...};

 // specialized allocator:
 template<class T>
 class Allocator : public UnsafeAllocator {
      ...
      virtual T& operator[] (const size_t * position) { ... }
 };

 // proxy class for the container:
 template<class TL>
 class Volume {
 protected:
     // each field has its own allocator, allocated in the constructor
     UnsafeAllocator *opaque_allocator[Length<TL>::value];
 public:
     // put a constuctor with the current dimensions here and fill in the array of allocators using the typelist

     // typesafe getter
     template <int index> 
     typename TypeAt<TL, index>::Result &
     get(const std::initializer_list<size_t> &position){
           typedef typename TypeAt<TL, index>::Result FieldType;
           typedef Allocator<FieldType> SafeAllocator;
           SafeAllocator* allocator = dynamic_cast<SafeAllocator *>(opaque_allocator[index]);
           assert(allocator != NULL);
           return (*allocator)[position];
      }
  };

  // usage would be:
  typedef MakeTypeList(float, int, double) types;
  // define a two dimensional volume for each type
  Volume<types> volume({1024,1024});
  // example of typesafe access for the first type at position 0,0:
  float f = volume.get<0>({0,0});

此实现的主要缺点是您需要在编译时知道所有类型列表。我不知道这是否属于您的规格。

如果不是,我认为没有任何方法可以让容器具有可变数量的可变参数类型而不使用任何地方的不透明代理,这会破坏代码的可读性(可能还有安全性)。 / p>