[英]Variadic Macro without __VA_ARGS__
所以,這基本上就是我想做的事情:
#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和兩個整數,類3可以用兩個bool構造,等等...)。
所有這些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”類的成員函數,如果不可用則引發異常(即,文件中不存在),否則返回值(從文件讀取)。
類“field”還有一個重載的運算符>>,它在值上應用標准運算符>>,並將布爾屬性設置為true。
由於模型很多,我想在宏中轉換代碼:-)
我不確定你是否可以自由地改變你的實現,就像我即將提出的那樣,但這就是我要做的。 它不需要任何奇特的模板技術。
首先,創建一個名為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
-type,從中派生所有其他類型,如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");
}
}
};
希望這可以幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.