I need to write a custom DSL in C++ that will be used to define Pokémon, abilities, teaching abilities to a specific Pokémon and then simulating the duel. So far, I’ve managed to make most of the DSL work as it was described, but I am having a hard time implementing the following code:
DEAR "Pikachu" LEARN [
ABILITY_NAME(Electric_Shock)
ABILITY_NAME(Lightning_Rod)
]
This DSL code should basically translate into valid C++ code that teaches the specified abilities to the Pokémon specified after the DEAR macro. What I have done so far for this specific part is the following:
/**
* \brief
*/
#define DEAR ; (*getPokemonByName(
/**
* \brief
*/
#define LEARN ))
/**
* \brief Retrieves the ability with the given name, if it exists.
* Otherwise, nullptr is returned.
* \param name The name of the ability to look for.
*/
#define ABILITY_NAME(name) getAbilityByName(#name)
The getPokemonByName() returns a Pokemon* if a Pokémon with the given name (following the DEAR macro) exists, or nullptr otherwise (I will emit an error and terminate before accessing the nullptr later). The same logic applies to the getAbilityByName() function, which in turn returns a SingleAbilityExpr*.
My thought process is to figure out a way to combine the various SingleAbilityExpr*s within the [] into one, and then overload the operator[] in the Pokémon class in order to teach the abilities to the selected Pokémon. The issue is that I cannot figure out a way to hack this functionality with this specific syntax. Is there an alternative to my approach that can achieve what I want?
Initially, I was hoping that if the ABILITY_NAME macro was to emit a , at the end, I could overload the operator, in the SingleAbilityExpr class and thus collect them this way. Sadly, this does not seem to work due to the last trailing comma, which I have found no way of consuming using c++11.
>Solution :
C++23 adds multi-argument operator[], but it’s useless in your case, because you’ll end up with following: (after adding a comma to ABILITY_NAME)
foo[
getAbilityByName("a"),
getAbilityByName("b"),
getAbilityByName("c"), // <--
]
Which is of no use, becaues the trailing comma is an error.
Instead you should do #define ABILITY_NAME(name) +getAbilityByName(#name), where getAbilityByName() returns a list of abilities (a wrapper around std::vector probably), with overloaded operators:
- Unary
+, which returns the list unchanged, and - Binary
+, which concatenates the lists.
Then operator[] should accept the list as the only argument:
foo[
+getAbilityByName("a") // Unary +
+ getAbilityByName("b") // Binary +
+ getAbilityByName("c") // Binary +
]