基于C++的DSL实现问题:宝可梦技能教学语法适配
C++自定义DSL宝可梦技能传授语法的实现方案
需要实现的自定义DSL技能传授语法:
DEAR "Pikachu" LEARN [ ABILITY_NAME(Electric_Shock) ABILITY_NAME(Lightning_Rod) ]
现有宏定义无法完成语法转换,尝试过重载逗号运算符但受限于C++11的尾逗号问题无法落地。现有相关代码:
#define DEAR ; (*getPokemonByName( #define LEARN )) #define ABILITY_NAME(name) getAbilityByName(#name) // 相关函数声明 Pokemon* getPokemonByName(const char* name); SingleAbilityExpr* getAbilityByName(const char* name);
方案一:利用初始化列表(推荐,C++11兼容)
核心思路是将DSL的[ ... ]转换为C++初始化列表,让Pokemon类提供接收初始化列表的技能传授方法,天然支持尾逗号。
步骤1:修改宏定义
#define DEAR getPokemonByName( #define LEARN )->learnAbilities({ #define ] }) #define ABILITY_NAME(name) getAbilityByName(#name)
步骤2:为Pokemon类添加learnAbilities方法
#include <initializer_list> class Pokemon { public: // ... 其他成员 ... void learnAbilities(std::initializer_list<SingleAbilityExpr*> abilities) { for (auto* ability : abilities) { if (ability != nullptr) { this->addAbility(ability); // 执行实际技能添加逻辑 } } } private: void addAbility(SingleAbilityExpr* ability); // 内部技能添加实现 };
展开效果
原DSL代码会被预处理器展开为合法C++代码:
getPokemonByName("Pikachu")->learnAbilities({ getAbilityByName("Electric_Shock"), getAbilityByName("Lightning_Rod") })
C++11完全支持初始化列表的尾逗号,即使DSL中最后一个ABILITY_NAME后加了逗号也不会报错。
方案二:可变参数模板(C++11兼容)
通过可变参数函数接收多个技能,避免依赖逗号运算符,适合不喜欢初始化列表的场景。
步骤1:修改宏定义
#define DEAR learnAbilitiesForPokemon(getPokemonByName( #define LEARN ), #define ] ) #define ABILITY_NAME(name) getAbilityByName(#name)
步骤2:实现可变参数辅助函数
// 递归终止函数 void learnAbilitiesForPokemon(Pokemon*) {} // 递归展开参数的模板函数 template<typename T, typename... Args> void learnAbilitiesForPokemon(Pokemon* p, T ability, Args... rest) { if (p != nullptr && ability != nullptr) { p->addAbility(ability); } learnAbilitiesForPokemon(p, rest...); }
展开效果
原DSL代码展开为:
learnAbilitiesForPokemon(getPokemonByName("Pikachu"), getAbilityByName("Electric_Shock"), getAbilityByName("Lightning_Rod") )
注意:C++11不支持函数参数的尾逗号,因此DSL中最后一个ABILITY_NAME后不能加逗号,这是该方案的局限性。
方案三:临时收集器对象
通过创建临时收集器对象,重载operator<<逐个接收技能,语法更贴近DSL的直观风格。
步骤1:定义收集器类
struct AbilityCollector { Pokemon* target; explicit AbilityCollector(Pokemon* p) : target(p) {} AbilityCollector& operator<<(SingleAbilityExpr* ability) { if (target != nullptr && ability != nullptr) { target->addAbility(ability); } return *this; } };
步骤2:修改宏定义
#define DEAR AbilityCollector(getPokemonByName( #define LEARN )) << #define ABILITY_NAME(name) getAbilityByName(#name) << #define ] ;
展开效果
原DSL代码展开为:
AbilityCollector(getPokemonByName("Pikachu")) << getAbilityByName("Electric_Shock") << getAbilityByName("Lightning_Rod") << ;
最后的<< ;不会报错,因为operator<<返回的是AbilityCollector&,C++允许表达式语句直接用分号收尾。
内容的提问来源于stack exchange,提问作者Stelios Papamichail
相关产品推荐
相关产品推荐

