You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 09:51:29