问题在于C++不是动态类型的语言,如何在运行时确定存储类型,参考知乎上的一些回答,了解到直接可以使用C++17支持的std::any/std::variant
来进行对未知类型的存储。其它的一些方法基本会通过union来实现一个结构支持不同的类型。
std::any§
我试验了使用any的情况,发现基于any的实现可以很容易的存入容器比如vector<std::any>
,但是当需要取出来时还是需要去对所有类型进行一个判断比较,使用std::any_cast
去得到对应的类型。
#include <iostream>
#include <vector>
#include <any>
#include <string>
using namespace std;
int main()
{
vector<any> anys;
int a = 10;
double b = 2.5;
char c = 'f';
string d = "Hello";
anys.emplace_back(a);
anys.emplace_back(b);
anys.emplace_back(c);
anys.emplace_back(d);
for (const auto& t : anys) {
if (t.type() == typeid(int)) {
cout << "int:" << any_cast<int>(t) << endl;
}
else if (t.type() == typeid(double)) {
cout << "double:" << any_cast<double>(t) << endl;
}
else if (t.type() == typeid(char)) {
cout << "char:" << any_cast<char>(t) << endl;
}
else {
cout << "string:" << any_cast<const string&>(t) << endl;
}
}
return 0;
}
union§
再就是看到梁笔记博客上的实现方法,贴过来。
定义一个结构AnyData,结构中定义状态Eflag(用于判断是存的是什么类型)和union(里面存数据)。
结构AnyData定义构造函数,为每种类型定义一个。
getType( )用于获取当前数据类型。
getVal( )重载函数用于获取实际数据值。
用STL vector容器为例,实现存放不同类型完整代码:
// 梁笔记
// https://zouzhongliang.com
#include <iostream>
#include <vector>
using namespace std;
enum EType{E_bool, E_char, E_short, E_int, E_long,
E_float, E_double};
struct AnyData
{
EType Eflag;
union {
bool bVal;
char cVal;
short sval;
int ival;
long lval;
float fval;
double dval;
};
AnyData(bool val){
Eflag = E_bool;
bVal = val;
}
AnyData(char val){
Eflag = E_char;
cVal = val;
}
AnyData(short val){
Eflag = E_short;
sval = val;
}
AnyData(int val){
Eflag = E_int;
ival = val;
}
AnyData(long val){
Eflag = E_long;
lval = val;
}
AnyData(float val){
Eflag = E_float;
fval = val;
}
AnyData(double val){
Eflag = E_double;
dval = val;
}
EType getType(){
return Eflag;
}
void getVal(bool& val){
if (Eflag == E_bool)
val = bVal;
}
void getVal(char& val){
if (Eflag == E_char)
val = cVal;
}
void getVal(short& val){
if (Eflag == E_short)
val = sval;
}
void getVal(int& val){
if (Eflag == E_int)
val = ival;
}
void getVal(long& val){
if (Eflag == E_long)
val = lval;
}
void getVal(float& val){
if (Eflag == E_float)
val = fval;
}
void getVal(double& val){
if (Eflag == E_double)
val = dval ;
}
friend ostream& operator<<(ostream& out, AnyData& data);
};
ostream& operator<<(ostream& out, AnyData& data){
switch(data.Eflag){
case E_bool:
out<< data.bVal;
break;
case E_char:
out<< data.cVal;
break;
case E_short:
out<< data.sval;
break;
case E_int:
out<< data.ival;
break;
case E_long:
out<< data.lval;
break;
case E_float:
out<< data.fval;
break;
case E_double:
out<< data.dval;
break;
}
return out;
}
int main() {
vector<AnyData> vecAnyData;
vecAnyData.push_back(AnyData(false));
vecAnyData.push_back(AnyData('q'));
vecAnyData.push_back(AnyData((short)12));
vecAnyData.push_back(AnyData(12));
vecAnyData.push_back(AnyData((float)2.1));
vecAnyData.push_back(AnyData(1.0));
for(unsigned int i=0;i<vecAnyData.size();i++){
cout<<vecAnyData[i]<<endl;
}
return 0;
}
输出结果:
0
q
12
12
2.1
1
总结§
通过以上两种实现是可以支持对任意的基本类型进行存放的,但不可避免的会有穷举判断类型的过程,对自定义类型,需要借助void*实现,在这里并没有介绍具体的过程,接触到具体情况再继续了解。
回到读取.csv文件的问题,通过以上两种方法能够支持对未知类型数据的存入,读出时再去类型枚举。对于首行列名即类型名进行解析是否有必要?建立一个类型序列,循环进行行的读取,但这样的问题是在存入时需要根据类型序列确定,存入到确定且相同类型的容器中,读出时也要根据类型序列确定类型。