所以,这基本上就是我想做的事情:
#define RS03(obj, a1, a2, a3) {if (_str1 == #a1) _file >> _##a1; if (_str1 == #a2) _file >> _##a2;if (_str1 == #a3) _file >> _##a3; obj (_##a1, _##a2, _##a3);}
这是三个参数的情况,但我还需要:
#define RS04(obj, a1, a2, a3, a4)
#define RS05(obj, a1, a2, a3, a4, a5)
#define RS06(obj, a1, a2, a3, a4, a5, a6)
...
这是一个可变的宏。
关于此主题的Stackoverflow有很多问题,但它们不适用于我的情况。
在上面的代码中,三个参数a1,a2和a3既用作字符串(在“if”条件下)又用作变量(在赋值和构造函数中),而obj是一个类(所以宏中的最后一个命令是构造函数调用。)
关键是:假设我有20个不同的类,要求每个类都有不同的输入数量来构建。
宏接收类的名称以及构建此类对象所需的参数名称。
关键是(参见下面的“设计原因”)我需要在“if”条件下使用参数的名称也像字符串一样。这就是我使用宏(具有#和##特殊字符的优点)而不是模板函数的原因。
基本上,我需要一个纯文本转换(所以是一个宏,而不是一个函数),但需要一个变量名称的参数。
让我们假设我有20个不同的类,每个类与另一个非常不同(例如,类1可以用两个double构造,类2需要一个double和两个整数,class 3可以用两个构造布尔等...)。
所有这些classe都有一个“run”成员函数,它产生相同格式的输出。
我的程序应该执行以下操作:
1 - 阅读文本文件
2 - 为文件中描述的每个模型启动运行
该文件应该是这样的:
car {
name = model1
speed = 0.05
}
man {
name = model2
speed = 0.03
male = true
ageclass = 3
}
...
所以我需要读取这样的文件并初始化文件中描述的每个类。
参数应该以用户喜欢的顺序写出。
此外,它们可能会出现多次,例如:
car {
name = pippo
speed = 0.05
speed = 0.06
speed = 0.07
}
(在这种情况下,最后一个将覆盖另一个)
如果用户忘记某些参数,程序应该以明确的错误消息停止。
可能存在相同类型的不同型号(例如,4种不同的平面型号)。
例如,在这种情况下:
car {
name = pippo
speed = 0.05
}
car {
name = pluto
}
程序应该说第二个模型不完整。
当然有很多方法可以做到这一点。
我这样做:
1 - 使用T成员(存储值)和bool成员(告诉我变量是否存在)创建模板类(让我们称之为“字段”)
2 - 使用多个“字段”创建一个对象(读取器,读取文件的对象),每个可能的模型属性一个(_name,_speed,_male等等)
3 - 然后,在读取文件时,对于括号内的部分,我首先读取一个字符串(“标签”),然后我读取“=”,最后我读取了值。该值将存储在具有相同名称的变量
中(如果我找到“speed = 0.03”这一行,我将在读者的字段_name中保存0.03)。
这应该是伪代码(可能有错误;它只是为了说明目的):
if (car) {
while (str != "}") {
if (str == "speed") { _file >> _speed; _file >> _str; }
if (str == "male") { _file >> _male; _file >> _str; }
if (str == "ageclass") { _file >> _ageclass; _file >> _str; }
ERROR;
}
car (_speed.get (), _male.get (), _ageclass.get ());
}
get()是“field”类的成员函数,如果不可用则引发异常(即,文件中不存在),否则返回值(从文件读取)。
类“字段”也有一个重载运算符>>,它应用标准运算符>>在值上,并将布尔属性设置为true。
由于模型太多,我想在宏中转换代码: - )
答案 0 :(得分:0)
我不确定你是否有自由改变你的实施方式,就像我即将提出的那样,但这就是我要做的。它不需要任何奇特的模板技术。
首先,创建一个名为Properies
的结构(例如),它包含任何类都可以禁止的所有属性。
struct Properties
{
enum Types
{
MAN,
CAR,
// and more
};
enum Gender
{
MALE, FEMALE
};
Types type;
string name;
double speed;
Gender gender;
int ageClass;
};
如您所见,它还包含一个描述每种现有类型的enum
。
接下来,您定义Base
- 类型,您可以从中导出所有其他类型,例如Man
,Car
等。
class Base
{};
class Man: public Base
{
string d_name;
Properties::Gender d_gender;
int d_ageClass;
double speed;
public:
Man(Properties const &properties)
{
// Set properties that apply to the "Man"-class
}
};
class Car: public Base
{
string d_name;
double d_speed;
public:
Car(Properties const &properties)
{
// Set properties that apply to the "Car"-class
}
};
每个类的构造函数都期望一个Properties
对象,从中提取适合它们的字段。例如,Car
构造函数不会检查gender
字段,而Man
构造函数将。
现在,您定义一个将处理解析的Loader
类。它包含一个成员readFile
,它返回Base*
的向量,这样您就可以指向一个容器中的所有已初始化对象。我选择shared_ptr
来处理所有权问题,但您应该决定什么是最适合您的应用程序。
class Loader
{
public:
static vector<shared_ptr<Base>> readFile(string const &fileName)
{
vector< shared_ptr<Base> > result;
ifstream file(fileName);
// Parse the file, creating a "Properties" object, called
// "props" here
while (file) // while EOF not reached.
{
Properties props = parse(file); // implement your parse
// routine, returning Properties.
switch (props.type)
{
case Properties::CAR:
result.push_back(shared_ptr<Base>(new Car(props)));
break;
case Properties::MAN:
result.push_back(shared_ptr<Base>(new Man(props)));
break;
// etc for all classes derived from Base
default:
throw string("error: unknown type");
}
}
};
希望这有帮助。