如何动态确定用于参数化模板的类型并进行比较 [英] How to dynamically determine the types used to parameterise a template and compare them

查看:21
本文介绍了如何动态确定用于参数化模板的类型并进行比较的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果根据一个或多个类型参数创建一个类模板,如何在运行时查询这些类型?

If one creates a class template based on one or more type parameters, how would one query these types at runtime?

例如:

template <typename T>
class Foo {
 public:
  typedef T TypeT;

  Foo() {}

  // assume i is in range
  void set(size_t i, T value) { store[i] = value; }
  T get(size_t i) { return store[i]; }

  void fast_copy(T* dest) { memcpy(dest, store, 100 * size_of(T)); }

  // ugly public data member
  T store[100];
};


void main() {
  Foo<double> fd;
  Foo<int> fi;

  // this is not C++!
  if (fd::TypeT == fi::TypeT) {

    // do a fast copy since the types match
    fd.fast_copy(fi.store);

  } else {

    // do a slower item by copy since the types don't match
    // and let the runtime perform the type conversion:
    for (size_t i = 0; i < 100; ++i) {
      fi.set(i, static_cast<fd::TypeT>(fd.get(i)));
    }
  }
}

我想这样做的原因是因为我有包含浮点数、双精度数或整数数组的运行时对象,我想将它们复制到不同类型的数组中,但我不知道这些类型是什么将在运行时确定.在上面的示例中,用于实例化 fi 和 fd 的类型是已知的,但在完整示例中 fi 和 fd 将引用多态类,该类可以是任何基本数字类型的数组的容器.

The reason I want to do this is because I have runtime objects that contain either arrays of floats, doubles or ints, and I want to copy them to an array of a different type, but I don't know what those types will be as they are determined at runtime. In the above example, the types used to instantiate fi and fd are known, but in the full example fi and fd would be references to a polymorphic class that could be containers of an array of any basic numerical type.

此机制旨在成为一组返回浮点数、双精度数或整数数组的函数与另一个需要浮点数、双精度数或整数数组的函数集之间的运行时可配置桥梁,以及桥两侧的类型可能相同,也可能不同.桥接器很重要,还可以做其他一些事情,但需要将一种类型的数组(或数组的容器)传入,并在另一种类型中生成另一种类型的数组 an(或数组的容器)结束,在类型相同和不同的情况下.

This mechanism is intended to be a runtime configurable bridge between a set of functions that return arrays of floats, doubles or ints, and another set that requires arrays of floats, doubles, or ints, and the types may or may not be the same on both sides of the bridge. The bridge is non-trivial and does a few other things, but needs to take an array (or a container of an array) of one type in, and produce array an (or a container of an array) of another type at the other end, in cases where the types are the same and also where they are not.

注意:这与我的上一个问题有关,如果这个问题更有意义,将替换它.

Note: this is related to this previous question of mine and will replace it if this one makes more sense.

这里有更多关于我正在尝试做的事情的信息.

here's a bit more info on what I'm trying to do.

我有一个系统,一方面由一组函数组成,这些函数返回数值结果.有些返回单个浮点数或双精度数,有些返回单个整数,有些修改(因此返回")浮点数、双精度数或整数数组.有一大堆.我想将每个(实际上,只是返回的数据,函数无关紧要)与我称为源"对象的东西相关联 - 这可能是 Source,其中 T是数字类型.虽然我可能需要 SourceScalarSourceVector 来处理单个值或数组.无论如何,这个对象是桥"的入口点.每个 Source 对象都存储在 Source 对象的异类集合中,并由唯一的Source Key"引用.例如,一个这样的与源相关的函数可能会返回一个包含 32 个浮点数的数组,表示波表振荡器的最新输出.另一个可能返回 32 个双精度数组,表示超过 32 个样本的峰值检测器输出.

I have a system that on one side consists of a set of functions that return numerical results. Some return a single float or double, some a single int, some modify (hence 'return') an array of floats, or doubles, or ints. There's a whole bunch of them. I want to associate each one (well, actually, just the returned data, the functions don't matter) with something I call a "Source" object - this might be Source<T>, where T is the numerical type. Although I might need SourceScalar<T> and SourceVector<T> to handle single values or arrays. Anyway, this object is the entry point to the "Bridge". Each Source<T> object is stored in a heterogenous collection of Source objects, and referenced by a unique "Source Key". For example, one such Source-associated function might return an array of 32 floats representing the latest output from a wavetable oscillator. Another might return an array of 32 doubles representing a peak detector output over 32 samples.

在系统的另一端,我还有另一组功能.这些都以数值为参数.有些需要单个浮点数或双精度数,有些需要单个 int,有些需要相同的数组.还有一大堆这样的.我有一个我称之为Dest"对象的东西 - Dest,或者 DestScalar, DestVector 如上所述.该对象存储一个与这些函数之一相关的 std::function 包装器,以便它可以在必要时调用该函数.每个 Dest 对象都存储在 Dest 对象的异构集合中,并由唯一的Dest Key"引用.例如,一个这样的函数可能是一个数字滤波器,它期望接收大小为 32 的数组,但不是浮点数的双精度数.

On the other side of the system I have yet another set of functions. These all take numerical values as parameters. Some require a single float or double, some a single int, some require arrays of the same. There's a whole bunch of these too. I have something I call a "Dest" object - Dest<T>, or maybe DestScalar<T>, DestVector<T> as above. This object stores a std::function wrapper that is tied to one of these functions, so that it can invoke the function when necessary. Each Dest object is stored in a hetereogenous collection of Dest objects, and referenced by a unique "Dest Key". For example, one such function might be a digital filter that expects to receive arrays of size 32, but of doubles not floats.

现在,在运行时,需要一个更高级别的系统(实际上由用户控制)来将任何 Source 与任何 Dest 任意关联.用户提供两个键 - 源和目标 - 系统将一侧的数据与另一侧的功能连接"起来.所以在上面的例子中,它可能会尝试将波表振荡器的 32-float 数组连接到数字滤波器的 32-double 数组参数.这个关联也可能随时被删除.如果来自峰值检测器的 32-double 阵列连接到数字滤波器的 32-double 阵列参数,那么它会希望传输/复制尽可能快,因为阵列的类型相同.

Now, at runtime, a higher-level system (actually controlled by a user) is required to arbitrarily associate any Source with any Dest. The user provides the two keys - source and dest - and the system 'connects' the data on one side with the function on the other. So in the example above, it might try to connect the 32-float array from the wavetable oscillator to the 32-double array parameter of the digital filter. At any time this association might be removed, too. If the 32-double array from the peak detector is connected to the 32-double array parameter of the digital filter, then it would want the transfer/copy to be as fast as possible because the arrays are of the same type.

编辑 2: 这里是一些编译代码,这是我可以构造的最简单的情况,它创建两个 Source 和 Dest 对象集合,并动态尝试连接"它们.对 source_collection[1]->set(...) 的前两次调用正常工作,但后两次不能正常工作,并最终调用 BaseDest 基类成员函数 set_item 而不是 Dest 中的那个.如果查询的类型相同,这也使用 typeid() 返回静态指针的观察,并比较这些指针的以确定类型匹配.这可能不安全,最好只使用枚举.

EDIT 2: here is some code that compiles and is the simplest case I can construct that creates two collections of Source and Dest objects, and dynamically attempts to 'connect' them. The first two calls to source_collection[1]->set(...) work correctly, but the second two do not, and end up calling the BaseDest base class member function set_item rather than the one in Dest<T>. This also uses the observation that typeid() returns a static pointer if the type queried is the same, and compares the values of these pointers to determine that the type matches. This is probably unsafe, might be best to just use enums instead.

这一切让我觉得很可怕——一定有更好的方法吗?

#include <vector>
#include <typeinfo>
#include <cassert>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>

class Source;    // fwd
class BaseDest;  // fwd

typedef boost::shared_ptr<Source>   SourcePtr;
typedef boost::shared_ptr<BaseDest> BaseDestPtr;

// target function that takes an array of doubles
void target_func_vd(double *array, size_t len) {
  for (size_t i = 0; i < len; ++i) {
    assert(array[i] == i);
  }
}

// target function that takes an array of floats
void target_func_vf(float *array, size_t len) {
  for (size_t i = 0; i < len; ++i) {
    assert(array[i] == i);
  }
}

// base class for Dest
class BaseDest {
public:
  BaseDest() {}
  virtual ~BaseDest() {}

  virtual void set(float *array, size_t len)  { /* not implemented, but can't be pure */ };
  virtual void set(double *array, size_t len) { /* not implemented, but can't be pure */ };

  virtual void set_item(size_t index, double item) { /* not implemented, but can't be pure */ };
  virtual void set_item(size_t index, float item) { /* not implemented, but can't be pure */ };
  virtual void finished(size_t len) = 0;

  virtual const std::type_info* get_type_info() const = 0;
private:
};

template <typename T>
class DestVector : public BaseDest {
public:
  typedef boost::function<void (T *, size_t)> Callable;

  explicit DestVector(Callable callable) : callable_(callable) { }

  virtual void set(T *array, size_t len) { callable_(array, len); }
  virtual void set_item(size_t index, T item) { buffer_[index] = item; }
  virtual void finished(size_t len) { callable_(buffer_, len); }

  virtual const std::type_info* get_type_info() const { return &typeid(T); };
private:
  Callable callable_;
  T buffer_[256];
};

// 'set' is overloaded by array type
class Source {
public:
  Source() {}
  void connect(const BaseDestPtr& dest) { dest_ = dest; }

  void set(float *array, size_t len) {
    if (dest_->get_type_info() == &typeid(double)) {
      // convert to double
      for (size_t i = 0; i < len; ++i) {
        dest_->set_item(i, array[i]);  // calls the base member function
      }
      dest_->finished(len);
    } else if (dest_->get_type_info() == &typeid(float)) {
      dest_->set(array, len);
    }
  }

  void set(double *array, size_t len) {
    if (dest_->get_type_info() == &typeid(float)) {
      // convert to float
      for (size_t i = 0; i < len; ++i) {
        dest_->set_item(i, array[i]);  // calls the base member function
      }
      dest_->finished(len);
    } else if (dest_->get_type_info() == &typeid(double)) {
      dest_->set(array, len);
    }
  }

private:
  BaseDestPtr dest_;
};


void main() {

  // test arrays
  float float_array[256];
  for (size_t i = 0; i < 256; ++i) {
    float_array[i] = static_cast<float>(i);
  }

  double double_array[256];
  for (size_t i = 0; i < 256; ++i) {
    double_array[i] = static_cast<double>(i);
  }

  // collection of Sources
  std::vector<SourcePtr> source_collection;

  SourcePtr s0(new Source());
  source_collection.push_back(s0);

  SourcePtr s1(new Source());
  source_collection.push_back(s1);


  // collection of Dests
  std::vector<BaseDestPtr> dest_collection;

  BaseDestPtr t0(new DestVector<float>(&target_func_vf));
  dest_collection.push_back(t0);

  BaseDestPtr t1(new DestVector<double>(&target_func_vd));
  dest_collection.push_back(t1);


  // create and invoke connections
  source_collection[0]->connect(dest_collection[0]);
  source_collection[0]->set(float_array, 256);  // this should end up passing float_array to target_func_vf, and it does

  source_collection[0]->connect(dest_collection[1]);
  source_collection[0]->set(double_array, 256); // this should end up passing double_array to target_func_vd, and it does

  source_collection[1]->connect(dest_collection[0]);
  source_collection[1]->set(double_array, 256); // this should end up passing double_array to target_func_vf, but it doesn't

  source_collection[1]->connect(dest_collection[1]);
  source_collection[1]->set(float_array, 256); // this should end up passing float_array to target_func_vd, but it doesn't
}

推荐答案

您可以使用重载.将此添加到您的班级

You can use overloads. Add this to your class

void copy(T & dest)
{
   fast_copy(&dest):
}

template<class U>
void copy(U & dest)
{
   for (size_t i = 0; i < 100; ++i) {
      dest.set(i, static_cast<TypeT>(get(i)));
}

PS:更多的c++方式是定义复制构造函数或赋值运算符

PS: more c++ way is to define copy-constructor or assignment operator

如果你想让动态的东西更加多态

If you want dynamic mke things more polymorphic

struct BaseDest
{
   virtual void assign(const double * v, size_t cnt) = 0;
   virtual void assign(const float * v, size_t cnt) = 0;
//etc
}

template<class T>
struct DestImpl
{
   void assign(const double * v, size_t cnt)
   {
       assign_impl(v, cnt);
   }
   void assign(const float * v, size_t cnt)
   {
       assign_impl(v, cnt);
   }

   template<class U>
   void assign_impl(const U * v, size_t cnt)
   {
      for (size_t i = 0; i < cnt; ++i) {
         set(i, static_cast<T>(v[i])));
   }

   template<>
   void assign_impl<T>(const T * v, size_t cnt)
   {
      fast_copy(v, cnt);
   }
//stuff
}

struct Source
{
//blah
   template<class T>
   void set(const T * v, size_t cnt)
   {
     dest_->set(v, cnt);
   }

//blah
}

这篇关于如何动态确定用于参数化模板的类型并进行比较的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆