• pjmlp 4 hours ago

    Factorial macro example in C++23 metaprogramming,

      #include <iostream>
    
      consteval long factorial (int n) {
        if (n == 0) return 1;
    
        return n * factorial(n - 1);
      }
    
      int main() {
        std::cout << factorial(7) << std::endl;
      }
    
    Exercise for the reader if using VC++ or clang/ninja, use import std instead.

    -- https://godbolt.org/z/TWe11hM6j

    Nicely put 5040 in ESI register at compile time.

    Granted, C++ isn't Lisp, but already has quite a room for creativity at compile time, and C++26 might finally have compile time reflection as well.

    • lispm 3 hours ago

      macro programming in Lisp would be "programming programs".

      I don't see it here. This looks like compile-time execution to compute values. If it would be a macro, it could return source code.

      • emmanueloga_ an hour ago

        Some people are calling this "multistage programming" [1] which is kind of related but not exactly the same as a macro system.

        --

        1: https://en.wikipedia.org/wiki/Multi-stage_programming

        • pjmlp 3 hours ago

          In general, a macro produces code at compile time, doesn't matter in which form it ends up in the binary, as long as the observable side effects are the same.

          Example of a library that generates serialization code, https://github.com/getml/reflect-cpp

          As mentioned the ongoing C++26 proposal with produce the desired source code at compile time, thus reducing the amount of code of libraries such that one.

      • thunkingdeep 3 hours ago

        Common misconception of non Lispers that macros are equivalent to compile time programming. You’re not simply moving the evaluation to compile time, you’re moving it upwards outside the program space into a higher dimension of programmability.

        Not to dog on C++ unfairly, CTE is pretty neat after all.

        Funnily enough, PGs “On Lisp” has some really neat macros in it that demonstrate capabilities that just can’t be replicated with template based macros, iirc.

        • nightowl_games an hour ago

          "outside the program space into a higher dimension of programmability"

          I can visualize this metaphor just fine, but I can't tell why it's useful. Can you make this concept more concrete?

          • eddd-ddde an hour ago

            I think an example would be helpful, conditionals:

            Within the "program dimension" there is just no way to run code conditionally without an if, no matter how much you move left and right, you are constrained. It is only possible by using the "higher dimension".

            • jcul an hour ago

              This is different from constexpr if in C++? Where one branch will not exist in the compiled code?

        • lispm 3 hours ago

              (defmacro factorial (n)
                (labels ((fact (m)
                           (if (= m 0)
                               1
                               (* m (fact (1- m))))))
                  `,(fact n)))
          
          The `, has no use here and can be removed. Here the backquote and the evaluation just returns the computed value.

          Thus, this is okay:

              (defmacro factorial (n)
                (labels ((fact (m)
                           (if (= m 0)
                               1
                               (* m (fact (1- m))))))
                  (fact n)))
          
          LABELS defines local recursive functions. The macro returns the result of calling FACT, which is a number and which is a valid form in Common Lisp. A number evaluates to itself.

              CL-USER > (macroexpand-1 '(factorial 10))
              3628800
              T
          • scott_s 5 hours ago

            Agreed with the technical content and conclusion. However, I think it is worth pointing out that since C++11, it has had a mechanism to specify (maybe) compile-time computations that are written in plain C++: constexpr, https://en.cppreference.com/w/cpp/language/constexpr

            (My parenthetical "maybe" is that I don't think compilers have to compute constexpr expressions at compile time. The compiler will be forced to when such expressions are used in contexts that require values at compile time. But I think it would be permissible for a compile to defer computation of a constexpr to runtime if the value isn't needed until runtime.)

            • spacechild1 4 hours ago

              Note that C++20 introduced consteval as a means to enforce compile time computation. See https://en.cppreference.com/w/cpp/language/consteval

              • pjmlp 4 hours ago

                Besides consteval and constinit, you can force evaluation with static constexpr.

              • liontwist 5 hours ago

                Lisp macros can take arbitrary parameters and are written in lisp.

                C++ macros can only take types and numbers (until variadic), and writing any code to operate on those inputs is challenging.

                • knome 4 hours ago
                  • Etheryte 4 hours ago

                    As a code golfer, this is beyond satisfying. Doing things in a few bytes is nice, yes, but doing them in a way no reasonable mortal ever would is even better.

                    • liontwist 2 hours ago

                      There is the lisp meme “mom can we have defmacro? No we have defmacro at home”

                    • dataangel 36 minutes ago

                      C++ templates can also take other C++ templates (the template itself, not just an instantiation), enum values and I think in C++23 maybe even structs but I need to check

                      • spacechild1 5 hours ago

                        The post is actually about template metaprogramming. 'template macroprogramming' isn't really a thing.

                      • mgaunard 2 hours ago

                        Does this article have a (2002) that I missed or something?

                        • James_K 3 hours ago

                          Macaroni art versus the Mona Lisa.

                          • forrestthewoods 4 hours ago

                            I don’t have any experience with Lisp. But I think C++ templates and Rust macros are both super bad and impoverished compared to what can be done in Jai.

                            https://www.forrestthewoods.com/blog/using-jais-unique-and-p...

                            • pjmlp 3 hours ago

                              Except Jai is never going to get the use any of them have, so we use what is there.

                              • forrestthewoods 5 minutes ago

                                That is not a coherent sentence.

                                I share it not to say “use Jai instead of C++/Rust”. But to instead say “templates and macros suck and it’d be great if Rust or other language copied good ideas into their ecosystem”.

                              • SkiFire13 4 hours ago

                                The `#modify` thing looks pretty cool, but I can't help but think how a compiler/ide is supposed to analyze the body of such function and provide suggestions. Rust macros have a similar issue, but it's offsetted by the fact that generics are pretty powerful too and can be fully analyzed by the compiler.

                              • qertuiopas 3 hours ago

                                Dudertiosa