It seems like doing this in C++ is overly complex. In language's like Nim where you can just executed ordinary code at compile-time it would be far simpler.
The author suggests he wants to "expand this little DSL later so does something a little more complicated using some non-C++ syntax", so Nim's ordinary code evaluation at compile time wouldn't help either. On the other hand, if he restricted himself to just using C++ expressions, he would be fine using constexpr (assuming he can use newer standards of the language).
That's what the OP meant with "Yes. That's right." In C++ circles, this loosely translates to "Let's use this ridiculously unwieldy if not downright stupid mechanism to do something just because we can".
I know it is fashionable to bash on C++ lately but I dont fully understand the motivation behind it. Could you please elaborate what drove you to write a post expressing your derision about C++? Does the frustration come from trying to use the language in the past and running into its limitations?
Hey dom96, while your intentions are probably fine, it would help to copy the first two lines of your HN bio here.
eg.
Disclaimer "I am one of the primary developers of the Nim programming language, as well as many tools and libraries for Nim. I am also currently writing a book about Nim called Nim in Action."
That carries both more weight to your answer as you clearly know Nim very well as well as gives people the correct perspective.
Does it extend to C or to Nim itself? Rust macros extend to Rust, and as an input it can receive any valid tokens (the only condition being to have all opening and closing () {} [] match correctly).
Rust procedural macros are one of the reasons I started using Rust.
It extends to Nim, and macros can also receive any valid Nim token. Alternatively you can also pass a string literal and parse it in your macro.
Nim's metaprogramming support is quite extensive, possibly even more so than Rust's. If this is something that interests you then you should check it out.
As an example of what's possible: async await in Nim is implemented entirely via a macro[1], the `=>` operator for closures is also implemented as a macro[2].
That's good to know, thanks. In many ways Nim resembles my ideal language that I wanted to make time ago. Rust is still above on my list, though. May give Nim a try if I get too frustrated with Rust.
Each new iterations of C++ introduces more "constexpr" elements. It's a C++ sub language to execute things "possibly at compile time". The language as a whole is too complex (ex: threads, futures, i/o) to fully execute at compile time without introducing its own set of insanities. Along with the future compile time introspection support it will improve.
For now, abusing of the template system is still popular.
I've always wondered why this isn't a first class language feature. You could make something like JOOQ or LINQ that just hooks the compiler itself at build time to extend the language arbitrarily. And no worries about outdated versions, you hooks could also polyfill newer syntax and features if you can hook at parse time.
This would result in a whole new breed of abominations but perhaps something decent would come of it
D has this use case solved in a really nice way. All you have to do is write a function that converts the DSL into D code and then do something like `mixin(my_dsl_converter('foobar'))`.
This may not be quite what the original poster is looking for, but boost::spirit allows one to build DSLs directly in the code. The DSL is then used to parse constructs and execute the results.