如果基于一个或多个类型参数创建类模板,如何在运行时查询这些类型?
例如:
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 将引用多态类,该类可以是任何基本数值类型的数组的容器。
该机制旨在成为一组返回浮点数、双精度或整数数组的函数与另一组需要浮点数、双精度或整数数组的函数之间的运行时可配置桥梁,并且类型可能相同也可能不同桥的两边。桥是不平凡的,它还做了一些其他的事情,但需要将一个类型的数组(或数组的容器)放入其中,并在另一个类型中生成另一种类型的数组 an(或数组的容器)最后,在类型相同和不同的情况下。
注意:这与我的上一个问题有关,如果这个问题更有意义,将替换它。
编辑:这里有更多关于我正在尝试做的事情的信息。
我有一个系统,它的一侧由一组返回数值结果的函数组成。有些返回单个浮点数或双精度数,有些返回单个整数,有些修改(因此“返回”)浮点数、双精度数或整数数组。有一大堆。我想将每一个(实际上,只是返回的数据,函数无关紧要)与我称之为“源”对象的东西相关联——这可能是Source<T>
,其中 T 是数字类型。尽管我可能需要SourceScalar<T>
并SourceVector<T>
处理单个值或数组。无论如何,这个对象是“桥”的入口点。每个Source<T>
对象存储在 Source 对象的异构集合中,并由唯一的“Source Key”引用。例如,一个这样的与源相关的函数可能会返回一个由 32 个浮点组成的数组,代表波表振荡器的最新输出。另一个可能会返回一个包含 32 个双精度数的数组,表示超过 32 个样本的峰值检测器输出。
在系统的另一端,我还有另一组功能。这些都以数值为参数。有些需要单个浮点数或双精度数,有些需要单个 int,有些需要相同的数组。这些也有一大堆。我有一个我称之为“Dest”对象的东西 -Dest<T>
或者可能DestScalar<T>
,DestVector<T>
如上所述。该对象存储了一个与这些函数之一绑定的 std::function 包装器,因此它可以在必要时调用该函数。每个 Dest 对象都存储在 Dest 对象的异构集合中,并由唯一的“Dest Key”引用。例如,一个这样的函数可能是一个数字滤波器,它期望接收大小为 32 的数组,但不是浮点数的双精度数。
现在,在运行时,需要一个更高级别的系统(实际上由用户控制)来任意关联任何 Source 与任何 Dest。用户提供两个键 - source 和 dest - 系统将一侧的数据与另一侧的功能“连接”起来。所以在上面的例子中,它可能会尝试将波表振荡器的 32-float 阵列连接到数字滤波器的 32-double 阵列参数。在任何时候,这种关联也可能会被删除。如果来自峰值检测器的 32-double 阵列连接到数字滤波器的 32-double 阵列参数,那么它会希望传输/复制尽可能快,因为这些阵列属于同一类型。
编辑 2:这是一些可以编译的代码,是我可以构造的最简单的情况,它创建两个 Source 和 Dest 对象的集合,并动态地尝试“连接”它们。前两个调用source_collection[1]->set(...)
正常工作,但后两个没有,最终调用 BaseDest 基类成员函数set_item
,而不是Dest<T>
. 这也使用了 typeid() 如果查询的类型相同则返回静态指针的观察,并比较这些指针的值以确定类型匹配。这可能是不安全的,最好只使用枚举。
这一切让我感觉非常可怕——一定有更好的方法吗?
#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
}