C++模板编程设计问题 - 根据输入文件返回不同的类型
C++ template programming design question - return different type according to input file
多媒体文件可能包含不同日期类型的数据,例如uint8_t,int16_t,浮点数等。 以下三个示例显示了第一个字节表示数据类型的文件内容:
1st File: 0,<uint8 data><uint8 data><uint8 data>...
2nd File: 1,<int16 data><int16 data><int16 data>...
3rd File: 2,<float data><float data><float data>...
FileReader 类读取文件并返回不同类型的 DataStream。我使用DataStreamBase,以便客户端持有指针。
////////////////////////////////////////////////
class FileReader {
DataStreamBase* readFile(string filename) {
switch (first_byte) { // the first byte in a file.
case 0:
return new DataStream<uint8_t>();
case 1:
return new DataStream<int16_t>();
case 2:
return new DataStream<float>();
case 3:
// ... there are many more "case <n>:"
}
return nullptr;
}
};
////////////////////////////////////////////////////
class DataStreamBase {
};
///////////////////////////////////////////////////
template<class T>
class DataStream : public DataStreamBase {
private:
T* data_;
};
///////////////////////////////////////////////////
// client
int main() {
FileReader reader;
DataStreamBase* stream = reader.readFile("some file name");
// Question: how to get a pointer to the data which may be uint8_t, int16_t, or float. Below approach is ugly.
//uint8_t* data = stream->getDataUint8();
//int16_t* data = stream->getDataInt16();
//float* data = stream->getDataFloat();
//...
}
客户端不知道输入文件是包含uint8_t、int16_t还是浮点数据,直到运行时。
问题: 客户端如何获取指向uint8_t、int16_t、浮点数、...哪些可以传递给第三方库?这种设计是解决此类问题的正确方法吗?谢谢。
最后,我选择了双重调度的解决方案。这不是原始问题的解决方案,但它避免了编写重复代码的问题。
使用双重调度的原因是客户端需要读取两个文件并通过传递两个数据流来调用第三方程序。第三方程序的接口为:
template<class T>
int calculate(const T* input_1, const T* input_2, vector<float>& result);
没有双重调度,客户需要像这样调用第三方程序:
DataStreamBase * ds1;
DataStreamBase * ds2;
if (auto cast_ds1 = dynamic_cast<DataStream<uint8_t>*>(ds1)) {
if (auto cast_ds2 = dynamic_cast<DataStream<uint8_t>*>(ds2)) {
calculate(cast_ds1->getData(), cast_ds2->getData(), result);
}
else if (auto cast_ds2 = dynamic_cast<DataStream<int16_t>*>(ds2)) {
calculate(cast_ds1->getData(), cast_ds2->getData(), result);
}
...
}
else if (auto cast_ds1 = dynamic_cast<DataStream<int16_t>*>(ds1)) {
if (auto cast_ds2 = dynamic_cast<DataStream<uint8_t>*>(ds2)) {
calculate(cast_ds1->getData(), cast_ds2->getData(), result);
}
else if ...
}
...
请注意,模板类 DataStream 定义方法 "const T* getData((",而基类 DataStreamBase 无法定义 getData(( 方法,因为它不知道返回类型是什么。
///////////////////////////////////////////////////
template<class T>
class DataStream : public DataStreamBase {
public:
const T* getData() const { return data_; }
private:
T* data_;
};
使用《现代C++设计》一书中的TypeList和StaticDispatch,我能够让编译器使用模板技术为我生成那些重复的代码。这个草图的想法:
客户端(主.cpp(:
using MyTypeList = TYPELIST_6(DataStreamBase<uint8_t>, DataStream<int16_t>, DataStream<int32_t>, DataStream<int64_t>, DataStream<float>, AudioStream<double>);
using DataStreamDispatcher = DoubleDispatcher<Calculator, int, std::vector<float>, DataStreamBase, DataStreamTypeList>;
DataStreamDispatcher::DispatchT1(*ds1, *ds2, calculator, calculate));
计算器.cpp:
class Calculator {
public:
// this api is 3rd party library.
template<class T1, class T2>
int calculate(const T1*, int, const T2*, int, std::vector<float>&);
template<class T1, class T2>
int applyDoubleDispatch(const T1& s1, const T2& s2, std::vector<float>& res)
{
return calculate(s1.getData(), s2.getData(), res);
}
...
};
双重调度.cpp:
template<class Executor, class ResType, class ParamType, class T1, class TypeList1, class T2 = T1, class TypeList2 = TypeList1>
class DoubleDispatcher {
public:
static ResType DispatchT1(T1& obj1, T2& obj2, Executor exec, ParamType& param) {
using HeadType = typename TypeList1::HeadType;
using TailType = typename TypeList1::TailType;
if (HeadType* t1 = dynamic_cast<HeadType*>(&obj1)) {
return DoubleDispatcher<Executor, ResType, ParamType, HeadType, TypeList1, T2, TypeList2>::DispatchT2(*t1, obj2, exec, param);
}
else {
return DoubleDispatcher<Executor, ResType, ParamType, T1, TailType, T2, TypeList2>::DispatchT1(obj1, obj2, exec, param);
}
}
static ResType DispatchT2(T1& obj1, T2& obj2, Executor exec, ParamType& param) {
using HeadType = typename TypeList2::HeadType;
using TailType = typename TypeList2::TailType;
if (HeadType* t2 = dynamic_cast<HeadType*>(&obj2)) {
return exec.applyDoubleDispatch(obj1, *t2, param);
}
else{
return DoubleDispatcher<Executor, ResType, ParamType, T1, TypeList1, T2, TailType>::DispatchT2(obj1, obj2, exec, param);
}
}
};
// see the book for code of specialization when T1 and T2 are not supported
相关文章:
- 如何使 windows 命令提示符在C++可执行文件上显示返回值?
- AcquireCredentialsHandleA() 返回 PFX 文件的0x8009030e(安全包中没有可用的凭据
- 配置文件解析器仅返回以前的值
- Glob 搜索目录也会返回文件
- C++模板编程设计问题 - 根据输入文件返回不同的类型
- 读取文件在第二次调用时返回INVALID_HANDLE
- 为什么TinyXML2的XMLDocument::FirstChild()函数在尝试解析这个有效的XML文件时返回NULL?
- 使用 sftp_open() 使用 C++ 中的 SFTP libssh 将文件从本地复制到远程时,File 返回 N
- 保存json文件后如何返回文件路径
- C++程序不返回文件中文本和字符串数据的值
- 查找第一个文件/查找下一个文件不返回文件夹中的所有文件
- 如何返回文件中项目的索引.dat
- 使用 firebreath 插件打开文件夹对话框并异步返回文件夹选择,这样 java 脚本就不会被阻止
- 如何写出文件并使其返回文件
- 从函数 C++ 返回文件 ID
- QFileSystemWatcher::files() 不返回文件列表
- 将对以字符串形式返回文件内容的函数执行移动语义优化或返回值优化,我会从中受益吗
- 从c++ dll返回文件流到Centura团队开发人员
- Boost::asio::async_read返回文件结束错误的换行符
- 文件对象->文件名不返回文件的完整路径