C Macro Reflection in Zig

302 points by jstrieb 11 months ago | 136 comments
  • skywal_l 11 months ago
    @cImport is on the chopping block though [0]. You will still be able to import c files but it will require a little more work. This is because they want this functionality out of the language so they can remove libclang dependency.

    [0]: https://github.com/ziglang/zig/issues/20630

    • samatman 11 months ago
      It will require a little more work in a trivial way, but not in a meaningful way.

      What's happening is that C imports are moving to the build system, instead of being a compiler builtin. It's part of making LLVM and libclang optional for programs which don't use it, but the use case of building C programs, and integrating C libraries with Zig programs, remains a central design goal.

      The build system is relatively new, and a lot of things which were originally independent subcommands are being consolidated into the build system.

      There's a sort of ambient impression that "Zig won't support C anymore" floating around, I'm not sure from your post whether you have that impression or not, but it isn't even vaguely true.

      It just means that importing C libraries will be a build step, and not something you write directly into the relevant source code. This is not a big deal.

      • jay-barronville 11 months ago
        > It just means that importing C libraries will be a build step, and not something you write directly into the relevant source code. This is not a big deal.

        It 100% is a big deal. I explained this in another comment [0].

        [0]: https://news.ycombinator.com/item?id=41111445

        • throwawaymaths 11 months ago
          I think you are operating from a mistaken understanding, as responded to in the linked comment.
          • samatman 11 months ago
            We have different ideas of what a big deal is, clearly.

            The idea that C programmers, C programmers, are going to start bailing en masse out of the top of the Zig funnel, because they have to copy-paste a build.zig gist to get started? This is risible.

            The complexity has moved to a slightly different place. That's it.

        • whitehexagon 11 months ago
          Do we know if this means the Reflection as mentioned in the article via @typeInfo will no longer work? or was it anyway comptime info? My Zig so far has been SoC level stuff, but using something like raylib or glfw is somewhere on my todo list, and this example sounds mighty useful. Anyway seems a strange thing to remove when Zig has the potential to be the new C. Hopefully also as stable for the next 30 years :)
          • pbaam 11 months ago
            It will still work. If you look at the type signature of @cImport in the language reference[1], it returns a type just as @import. So you can call @typeInfo on it. But instead of writing

              const win32 = @cImport({
                @cInclude("windows.h");
                @cInclude("winuser.h");
              });
            
            You will write:

              const win32 = @import("win32");
            
            Where the module "win32" is declared in build.zig.

            [1] https://ziglang.org/documentation/master/#cImport

          • deagle50 11 months ago
            It seems the build system has gotten enough traction and Andrew is going for broke to enshrine its place in the C space. I wouldn't bet against him.
            • ajnin 11 months ago
              > remove libclang

              So `zig cc` will have to go as well ? I was under the impression Zig as a drop-in C (cross-)compiler was one of its main selling points.

              • flohofwoe 11 months ago
                See: https://github.com/ziglang/zig/issues/16270#issuecomment-161...

                The ability to seamlessly compile C/C++/ObjC code within a Zig project is extremely important for me as well, but I'm fine with that job being delegated to a Zig package and the Zig build system.

                • ajnin 11 months ago
                  I read that commment as well but if you need to use the Zig build system it means you won't be able to integrate Zig into an existing project as easily, you'll have to integrate the Zig build system or even to integrate your project into the Zig build system, and that's an another level of complexity entirely.
                • pta2002 11 months ago
                  I think for better or for worse it makes sense that this is delegated to a separate package. For me it always seemed like a weird tangential thing that Zig did that is not really related to Zig The Language, and more to Zig The Build System. It made more sense when they were depending on LLVM either way, but now that they're closer to getting rid of it, it does not make sense to keep the dependency just for something that always seemed more like a 'neat thing' than a core language feature.
                • jay-barronville 11 months ago
                  While I understand the reasoning, I think this is one of the most disappointing decisions by the Zig team.

                  One of the main reasons I took Zig seriously was their C interop story—as someone who loves C and dislikes almost every implementation of C interop and FFI I’ve used in other languages (Rust is a notable exception to this), I was pretty much sold on Zig when I was able to, in a total of < 3-ish hours, (1) implement a complete Zig wrapper over a C library I wrote without introducing any overhead, (2) build a Zig program using the wrapper, and (3) cross-compile the program, statically, producing single binaries for 5 different targets…Linux (amd64, arm64), macOS (amd64, arm64), and Windows (amd64), ALL from one laptop using a single Zig executable and some simple flags. This was C interop and productivity I’ve never experienced before.

                  I respect Andrew a lot (I think he’s pretty brilliant), so I hope this turns out to not be as bad as I think it’ll be for those of us who love Zig for what brings to the table for programmers who are biased toward C.

                  • Laremere 11 months ago
                    See these posts on the LLVM issue by Andrew. They clearly state that this functionality isn't going anywhere, just the method of achieving said functionality is changing:

                    https://github.com/ziglang/zig/issues/16270#issuecomment-161...https://github.com/ziglang/zig/issues/16270#issuecomment-161...

                    • ajnin 11 months ago
                      Not involved with Zig at all, but this comment is a bit concerning :

                      > This is not the first controversial change I have made to the Zig project, and it won't be the last. I have a vision, and I know how to execute it. People are often surprised by what Zig has accomplished, and they wonder why other projects have not done what Zig does. Well, you are seeing the magic right now. I ignore the peanut gallery and do what I know is right, while taking care of my users' needs at the same time. If you don't understand now, you will understand once this plan unfolds. By that time it will seem obvious in hindsight.

                      He's probably brillant and all but this ... feels like hubris.

                      • kasajian 11 months ago
                        Sounds like damage control. I have to say, for a brilliant guy, andrewrk knows nothing about marketing. The fact that the Zig project can make this significant of a change to their core-value because "trust me i know what I'm doing", makes it impossible (for now) to rely on this in any type of a widely-deployed Enterprise setting. This just made the highly risky move of moving to Zig make it darn near impossible.

                        What he should have done is announce a project at the same time that will maintain the current developer experience, even if that project is not part of the zig foundation. The developer doesn't care about how he builds zig. They want 1) download 2) use. It doesn't matter from where. If today it's directly from zig, and tomorrow it's from elsewhere to download a combined package, that 's all the devs needed to hear.

                      • brabel 11 months ago
                        > as someone who loves C and dislikes almost every implementation of C interop and FFI I’ve used in other languages (Rust is a notable exception to this),

                        Have you tried D? It can import C files as if they were D modules: https://dlang.org/spec/importc.html

                        Basically, if there's a `hello.c` file next to your D file, you simply import it with:

                            import hello
                        
                        And use the functions it provides. You can also import only a subset of it, of course:

                            import hello: square;
                        
                        Or rename the import:

                            import hi = hello;
                        
                        
                        The C stdlib is exposed by D's stdlib as if it were a D library: https://dlang.org/phobos/core_stdc_assert_.html

                        C libraries (header files) can be compiled to D and then used as D modules as well, see https://github.com/jacob-carlborg/dstep

                        Is that as good as Rust?

                        • jay-barronville 11 months ago
                          > Have you tried D?

                          I tried D years ago (at least 5 years ago, I think), but I don’t remember experimenting with the C interop.

                          Your explanation sounds intriguing though. And a couple pretty smart folks I respect have talked about D too. So thanks for bringing it up. I’m going to make some time to experiment with it again some time soon.

                          > Is that as good as Rust?

                          Franky, although I don’t have firsthand experience with D, your explanation and example make the D C interop experience sound actually better than Rust’s. It seems more similar to Zig.

                        • jll29 11 months ago
                          This sounds amazing, and it's great that tooling is so strong of some "new kids on the block" (Zig, Rust), even better than C.

                          With hindsight, it is strange that the C community, with all the people and money behind it (and what scale!) never even managed to build a proper packaging manager (okay, there's now Conan, but that came from Python guys).

                          But then, there even still isn't a perfect C string library around (something that would combine GLib, SDS, ICU, say, and then standardize it in C2038).

                          [1] Hanson's C: Interfaces & Implementations (CII) - Str: https://cii.s3.amazonaws.com/book/pdf/quickref.pdf

                          [2] ICU - https://icu.unicode.org

                          [3] SDS - https://github.com/antirez/sds

                          [4] GLib - https://docs.gtk.org/glib/struct.String.html

                          • bluGill 11 months ago
                            > With hindsight, it is strange that the C community, with all the people and money behind it (and what scale!) never even managed to build a proper packaging manager (okay, there's now Conan, but that came from Python guys).

                            Package managers are a lot more complex than people realize. Every attempt I've seen in every language has significant lacks for common real world cases.

                            Most commonly they assume all the world is their language. Very commonly they want to take over all package management but don't have a good easy story for working with the OS package manager (I know Windows doesn't really have a package manager - but you still need to interoperate with it!). Those are big issues anyone in a complex project will face in the real world, there are also lots of little warts that will bite you.

                            Yes package managers are nice on your trival project. However they all get in the way in your non-complex project - some more than others.

                            • flohofwoe 11 months ago
                              The interesting thing is that the Zig build system could be implemented in a C/C++ compiler without any language changes. All that's needed is that (for instance) Clang would have a new option `clang build` which looks for a build.c/cpp file, compiles that into an executable and runs it.

                              The actual build system functionality is 'just' an extension to the stdlib.

                              • uecker 11 months ago
                                In the unix world we use distribution package managers. This has many advantages, including security updates, some robustness against supply chain attacks, large-scale integration. All this language-level packaging systems are a mistake in my opinion.
                                • fxtentacle 11 months ago
                                  vcpkg + CMake ?

                                  It can compile almost any dependency on demand, even doing cross-compilation. And it's used by at least Microsoft and Google.

                                • Cloudef 11 months ago
                                  How I see is that the only thing that changes is that you can't do @importC anymore. You'll instead do something in build.zig that produces a module which you can then addImport("my-c-lib", generated_module); which you then @import("my-c-lib"); in your zig code as you would with @cImport.

                                  This does not seem bad in paper. One thing that does worsen with this is that in @cImport you could also comptime define preprocessor macros which was really cool, now that would have to be handled by build.zig I guess.

                                  • jay-barronville 11 months ago
                                    This makes the experience more similar to Rust (which I don’t think is bad—it’s just not as unique, smooth, and impressive as the current Zig experience).

                                    I’ve been able to convince C programmers to try out and use Zig just due to this unique ability alone, and to be clear, getting C programmers to seriously consider any language other than C is generally very difficult!

                                    Having to consider another build system (with its own semantics), which the current Zig experience doesn’t require, changes the experience much more substantially than I think the Zig team realizes.

                                    • jeltz 11 months ago
                                      Having worked with Rust I would say that is bad. @cImport is way better than the C introp in Rust.
                                      • throwawaymaths 11 months ago
                                        You will probably be able to do it from the command line too (for build-exe, run, etc).
                                      • flohofwoe 11 months ago
                                        The translate-c build system step and wrapping the output in a Zig module is currently about 10 lines in build.zig, and I guess this could be reduced further by merging those two steps into a single step.

                                        I think that's an acceptable compromise.

                                        Especially for C libraries which require configuration via preprocessor defines or compilation flags, the build.zig way is a lot cleaner than the @-builtins that are currently used (IMHO).

                                        • 1980phipsi 11 months ago
                                          D has made some improvements on C interop with importC, if you want to check that out: https://dlang.org/spec/importc.html
                                          • throwawaymaths 11 months ago
                                            What's the problem? @cImport is becoming just @import.
                                            • jay-barronville 11 months ago
                                              > What's the problem? @cImport is becoming just @import.

                                              You’re oversimplifying what’s actually a significant change.

                                              Right now, I can do the following:

                                              1. Download a prebuilt Zig release [0], on a fresh computer (zero prerequisites).

                                              2. Implement a C library foo via foo.h and foo.c.

                                              3. Compile foo.c into an object file foo.o via `zig cc`, using standard GNU-compatible compiler flags, for virtually any of the common (and even not-so-common) compilation targets.

                                              4. Implement a Zig program bar via bar.zig.

                                              5. Directly import foo.h into bar.zig via `@cImport` and use the C API as if it was Zig code.

                                              6. Compile bar.zig and foo.o into a statically-linked executable baz via `zig build-exe`, for virtually any of the common (and even not-so-common) compilation targets.

                                              No knowledge of the Zig build system and its semantics necessary. This is one of the most attractive aspects of Zig for a C programmer. I’ve gone through these exact steps with C programmers who thought Zig seemed interesting in theory but just didn’t want to have to learn a new ecosystem, build system, etc. The moment I showed them how quickly they could get up and running with Zig (and test it out with C code), without even having to install anything, it suddenly was impressive enough to experiment with.

                                              The build system requirement may seem minor if you’re already a Zig programmer, but it’s massive if you want to attract C systems programmers.

                                              [0]: https://ziglang.org/download

                                              • kbolino 11 months ago
                                                @cImport works without build.zig and even with build.zig requires no special configuration (ignoring linking).

                                                As it is currently described, @import of C will require build.zig and specific configuration therein.

                                            • flohofwoe 11 months ago
                                              It would actually be nice if the translate-c build system step would also allow some restricted symbol transformation (at least stripping the 'namespace prefix' from C library symbols).

                                              For instance Odin allows to define a 'link_prefix' for C APIs which is then stripped from the imported symbols:

                                                  @(default_calling_convention="c", link_prefix="sg_")
                                              
                                              This causes name transformations like:

                                                  sg_setup() => setup()
                                                  sg_shutdown() => shutdown()
                                              
                                              ...maybe even convert from common case-conventions (like snake-, camel-, pascal- case etc...) to Zig's naming convention so that imported C interfaces don't look so alien relative to native Zig interfaces.
                                              • Cloudef 11 months ago
                                                From the issue

                                                > As a consolation prize, the TranslateC build step can be enhanced with advanced settings, such as namespace stripping and validation of existing bindings. Since changes to this don't require changing the language, it's OK if the scope & complexity increase to some extent.

                                                • o11c 11 months ago
                                                  > common case-conventions

                                                  Unfortunately, we must not forget forget ABOMINATIONCase and NAMESPACED_PascalCase (we could also generalize this to any switch from one convention to another after the first word). I've found they usually are, in fact, identifiable and roundtrippable. I've found the following usually works (it fails for multi-word prefixes or non-leading exceptions, and of course single-word identifiers are ambiguous):

                                                    count and strip all leading, then trailing, symbols (in some languages this is not limited to underscore but said languages usually need special handling anyway)
                                                    for every chunk separated by underscore, space, or hyphen (this may be nothing):
                                                      if the chunk either has no uppercase or has no lowercase, simply use it as a word. Otherwise:
                                                      for every sliding-window pair of letters (c, d) in the chunk:
                                                        if c is uppercase:
                                                          if d is lowercase, or d is a digit and there is lowercase elsewhere:
                                                            start a new word before c
                                                  
                                                  Then for the words-to-convention direction:

                                                    space and kebab case don't have any good answer for affixes AFAIK. Otherwise, restore at least the leading underscores (trailing underscores are usually keyword-avoiding)
                                                    for snake and screaming case, be sure to prepend an underscore if the *result* would start with a digit
                                                    for camel variants, prepend an underscore to each *word* that starts with a digit. But if there were originally more than 1 leading underscores, use those instead for the first word.
                                                  • flohofwoe 11 months ago
                                                    I mean, there can always be a function on the TranslateC step which maps 'special case' names (or even translate them to an entirely different name, for instance when there's collisions with Zig reserved keywords):

                                                        translateSpecialCaseNames(.{
                                                            .{ .src = "ABOMINATIONCase", .dst = "case" },
                                                            .{ .src = "NAMESPACED_PascalCase", .dst = "pascalCase" },
                                                        });
                                                    
                                                    ...in my own language bindings generator, being able to define such special case mappings is actually quite important, mainly for handling that situation where an automatic mapping would result in a reserved keyword.
                                              • WalterBright 11 months ago
                                                Example from the article:

                                                    const win32 = @cImport({
                                                        @cInclude("windows.h");
                                                        @cInclude("winuser.h");
                                                    });
                                                
                                                    pub fn main() !void {
                                                        _ = win32.MessageBoxA(null, "world!", "Hello", 0);
                                                    }
                                                
                                                Equivalent D:

                                                    import windows, winuser;
                                                    void main() {
                                                        MessageBoxA(null, "world!", "Hello", 0);
                                                    }
                                                
                                                In essence pared it down to the essentials. The compiler figures out the rest.

                                                Sometimes people ask for a special syntax for importing C files, but I like this simplicity so much better.

                                                • throwawaymaths 11 months ago
                                                  Some of us like explicit invocations, not being unsure if something comes from a .h filenor some other importing mechanism (what if a .h file collides with another import mechanism)

                                                  Naked imports are also annoying. Which import did that MessageBoxA come from? windows? or winuser? Is it in the language kernel?

                                                  Explicit is better than implicit. The utter pain for the code writer of four or five keystrokes here and there is not worth confounding the code reader.

                                                  • WalterBright 11 months ago
                                                    Imports are found along the search path (just like .h files are searched for by the C preprocessor). The first one found it the one selected (just like the C preprocessor does).

                                                    > Which import did that MessageBoxA come from?

                                                    If it exists in two or more imports, the compiler will give an ambiguity error. To resolve the ambiguity error, qualify the call with the name of the import:

                                                        windows.MessageBoxA(null, "world!", "Hello", 0);
                                                    
                                                    or, one can do this:

                                                        import windows : MessageBoxA;
                                                    
                                                    or this:

                                                        import windows, winuser;
                                                        alias MessageBoxA = windows.MessageBoxA;
                                                    
                                                    You can be as explicit as you like, and don't need to worry about ambiguity because the compiler will issue an error for that. It works the same way for D imports.
                                                    • throwawaymaths 11 months ago
                                                      Compiler giving a compiler error still favors the writer, not the reader of code. It seems like in its design in general D favors the writer of code with all its complicated bells and whistles that one must keep in mind as a reader. I think decades of experience has shown us that it is way better to favor the reader.
                                                • voidUpdate 11 months ago
                                                  I really want to like zig, but I've just had some annoying problems with it, most of which I think are just a result of it not being in 1.0 yet. For example, the recommended way to start a project, with `zig init`, has a load of code that I really don't need when I just want a bare project to get started. I only recently found out that you can just `zig build-exe filename.zig` and skip the whole init part. Also I've had a lot of issues getting editor integration to work correctly. I've installed the VSCode extension but I don't seem to be getting autocomplete etc. It is quite possibly just an ID-10T problem though, so I'll probably take another look at it some weekend
                                                  • dgb23 11 months ago
                                                    > For example, the recommended way to start a project, with `zig init`, has a load of code that I really don't need when I just want a bare project to get started.

                                                    I recently started a new project. zig-init provides you with a working build.zig file and two very minimal project files under /src, one of which is a hello world binary and the other is a hello world library.

                                                    > Also I've had a lot of issues getting editor integration to work correctly.

                                                    ZLS is not at the same level of what you would expect from a more mature language. For example by default it will not help you with anything related to comptime.

                                                    I highly recommend reading this fairly recent blog post by Loris Cro:

                                                    https://kristoff.it/blog/improving-your-zls-experience/

                                                    It explains how you set up your build.zig and ZLS (a few lines of configuration) in order to get much more help from ZLS. It will then perform the equivalent of what `cargo check` (Rust) does.

                                                    • voidUpdate 11 months ago
                                                      Really? I get a main.zig file with a hello world as well as some maths, and a build.zig that has stuff that I can't work out how to correctly get rid of. I'm not currently at that workstation so I can't give specifics though. I feel it would be easier if it was literally just a hello world print and the build literally just built it, rather than doing tests on code I already know works, because it shipped with the language
                                                      • dgb23 11 months ago
                                                        Yes, that's what I meant. But I think I talked passed you in a way.

                                                        I think its fair criticism that as a beginner, you don't actually want to deal with build.zig or even understand it. You just want to write some code and run it and look at build.zig after you gained some familiarity with the language itself.

                                                    • flohofwoe 11 months ago
                                                      Tbf, when coming from the C/C++ ecosystem, these types of problems are 'just another Tuesday' (especially for cross-platform projects and the official VSCode C/C++ extension).
                                                      • jeroenhd 11 months ago
                                                        This is part of the reason why I don't use VSCode for C(++). Jetbrains Clion seems to be the best IDE for those languages, with Visual Studio (the full fat one, costing a grand, as the free version lacks a bunch of features) as a close second, depending on what platform you're developing for.

                                                        VSCode is great when it works, but in cases like these it quickly becomes obvious that it's a hodge-podge of tools glued together via extensions and command lines rather than a purpose-built IDE.

                                                        • Quothling 11 months ago
                                                          I'm not one to defend VSC as such, but I do think it's fair to mention that a lot of the Zig issues have been directly with the Zig LSP (zls) and not necessarily with any specific editor extension. Though I suppose some of it may be worse with VSC as it's often been rather important to keep both your Zig and zls versions up-to-date and synchronized. I'm not sure how that happens with the VSC extension, but if it auto-updates your zls version (which it probably does) then it may race ahead of your Zig version which might cause problems.
                                                          • markus_zhang 11 months ago
                                                            I have never used Clion. Can you please share why you believe it is the top of the crop? I'm writing a C++/SDL2 game engine in Visual Studio but I think the IDE is really slow and error prone even for a small project. Everything just runs so slow. Maybe I need a better machine though.
                                                          • bobajeff 11 months ago
                                                            Note for people wanting to use vscode for c or c++ development. The clangd extension* is provides much better code completion and jump to definition compared to the default c/c++ extension.

                                                            * https://marketplace.visualstudio.com/items?itemName=llvm-vs-...

                                                            • voidUpdate 11 months ago
                                                              Yeah, that's one of the reasons I dislike the C++ ecosystem so much and want to like Zig haha
                                                            • jay-barronville 11 months ago
                                                              > […] I've had a lot of issues getting editor integration to work correctly. I've installed the VSCode extension but I don't seem to be getting autocomplete etc. […]

                                                              If you use ZLS [0], make sure you’re always using the right version for the Zig version you have installed on your machine. In my experience, that fixes 90% of editor issues I’ve encountered using Zig (I don’t use Visual Studio Code though, so it’s possible your editor issues are related to the editor itself).

                                                              [0]: https://github.com/zigtools/zls

                                                              • Cloudef 11 months ago
                                                                zls should generally work out of the box but I don't use vscode, so your mileage may vary. Make sure your zls and zig versions match. zig build-exe, zig run can be fine for small things, but when you want to start importing modules or do something more fancy, build.zig is nice to have.
                                                                • voidUpdate 11 months ago
                                                                  According to the extension, ZLS is optional, but according to some zig docs I found, its part of the extension. I may have misread, I'll try this stuff again sometime
                                                                  • brabel 11 months ago
                                                                    As with all VSCode language extensions, they will try to download and manage ZLS for you. That's ok if it's done well, but they probably have some ways to go still.

                                                                    I just prefer using Zig on emacs with the built-in eglot LSP client. You just tell it where you installed ZLS (and it won't do magic to get it for you) and off you go. It's the same level of support as with VS Code. If you're not new to emacs, maybe consider that (otherwise emacs may be too much of a rabbit hole :)).

                                                                • jeroenhd 11 months ago
                                                                  I have the same issue. It's hard to say if the tooling is working but incomplete, or if the tooling doesn't work for some reason. I can get syntax highlighting to work, but basic variable autocomplete doesn't, so I'm guessing the language server ran into some kind of issue.

                                                                  I really want to get started with Zig but I don't want to go back to the age of nano/edit.com for learning a new language. Zig is complex enough already.

                                                                  • samatman 11 months ago
                                                                    For what it's worth, outside of some outstanding issues with comptime (which are genuinely difficult, if ultimately solvable), I've found autocomplete, go-to-definition, and the other LSP semantic tools, to work fine in vs-code.

                                                                    You could hop on the Discord and get some help with your configuration if you'd like. All things Zig are somewhat underdocumented currently, that could use some polish for sure.

                                                                  • laeri 11 months ago
                                                                    In this case you probably haven't looked at it properly. There is just the starting entry main.zig, and a root.zig file which you can remove if you don't need it. Nothing complicated or annyling problems at this stage.
                                                                    • nindalf 11 months ago
                                                                      Honestly same. I think it has the potential to be a great language and I'll definitely have a look at 1.0. Right now it's more for the people who are fine living on the bleeding edge.

                                                                      Nothing wrong with that, imo. It's hard to evolve a language without having the ability to make breaking changes. They're doing the right things, generally making the right calls, and taking their time building instead of rushing.

                                                                      • 11 months ago
                                                                      • Joker_vD 11 months ago
                                                                        Clang's preprocessor is actually not implemented as a separate compilation pre-pass, it's essentially a part of the lexer and I would be willing to bet that gcc uses a similar scheme.

                                                                        So there is nothing technically impossible about having the access to macro names as a compiler-specific extension, it's just that there is no much demand for it.

                                                                        • JonChesterfield 11 months ago
                                                                          Clang prints out things about macro instantiations on the semantic error reporting paths. That probably means it has all the macro information available already.

                                                                          It's not probably reflected to the language because C++ fears reflection in general and hates macros in particular.

                                                                          • bluGill 11 months ago
                                                                            Reflection is on track for C++26 so it is completely wrong to say C++ fears reflection.
                                                                            • Joker_vD 11 months ago
                                                                              Reflection proposals have been around at least since 2014, so I'd say it's exactly right to say that C++ fears it — otherwise it'd have arrived much sooner.
                                                                            • g15jv2dp 11 months ago
                                                                              > C++ fears reflection in general and hates macros in particular.

                                                                              Macros aren't a particular case of reflection... And at least in the way they're done in C++, they're a big source of bugs / spaghetti.

                                                                              • formerly_proven 11 months ago
                                                                                > C++ fears reflection and hates macros

                                                                                But only wicked gods would banish us from paradise

                                                                                Why do they fear our power?

                                                                                Because evil is what they are.

                                                                            • Uptrenda 11 months ago
                                                                              Those function definitions really look amazingly readable. I've seen this done before in other languages and its usually quite horrible. Maybe Zig is worth learning? This is a killer feature.
                                                                              • david2ndaccount 11 months ago
                                                                                I wrote a blogpost showing how you could do a similar thing in D with ImportC.

                                                                                https://www.davidpriver.com/C-macro-reflection-in-D.html

                                                                                • eska 11 months ago
                                                                                  Wouldn’t this add at least UINT16_MAX*sizeof(intptr_t) bytes into the executable per enum?
                                                                                  • Cloudef 11 months ago
                                                                                    It adds 65536 pointers to the binary. Alternative would be to use a hash map. I think if they made function that used inline for instead it would optimize to a switch. No need for a LUT.

                                                                                       fn stringFromMsg(umsg: c_int) [:0]const u8 {
                                                                                           @setEvalBranchQuota(1000000);
                                                                                           inline for (@typeInfo(win32).Struct.decls) |field| {
                                                                                               if (field.name.len >= 3 and std.mem.eql(u8, field.name[0..3], "WM_")) {
                                                                                                   if (umsg == @field(win32, field.name)) {
                                                                                                       return field.name;
                                                                                                   }
                                                                                               }
                                                                                           }
                                                                                           unreachable; // umsg is not valid, programming mistake
                                                                                       }
                                                                                    
                                                                                    godbolt: https://zig.godbolt.org/z/7b73aoosf

                                                                                    In zig-budoux, I also do comptime reflection on cImport struct to assert compile time that we won't produce broken runtime code

                                                                                    https://github.com/Cloudef/zig-budoux/blob/master/src/c.zig#...

                                                                                    • flohofwoe 11 months ago
                                                                                      > ...instead it would optimize to a switch. No need for a LUT.

                                                                                      IME there's is no difference in (optimized) code generation between an if-else chain, a switch or (like in your example) an unrolled for-loop with an if inside. All those high level constructs will be optimized into a single lookup table, or a combination of multiple lookup tables and binary search to select between those. Only if there are absolutely no consecutive ranges in the switch-set, a binary search without jump tables will be used.

                                                                                      • Cloudef 11 months ago
                                                                                        Note that you can't use normal for here as you are accessing comptime known variables. Inline for will unroll the loop so that comptime constants get resolved for testing against runtime variables and then the optimizer picks out the best code (often jump tables / switch) to generate. You are right though.
                                                                                    • flohofwoe 11 months ago
                                                                                      I think the executable will essentially contain a lookup table with 2^16 slots and the string data for each macro name matching WM_* in Windows.h. But it's hard to come up with a better solution since the actually required names are unpredictable at compile time. The size of the lookup table could be reduced though if the WM_* values occupy a much smaller range.
                                                                                    • classified 11 months ago
                                                                                      Thank Apple for Reader View in Safari. If you are as incompetent in visual design as the author of that page, you should really stay away from dark mode. Your readers will thank you.
                                                                                      • flohofwoe 11 months ago
                                                                                        What's wrong with green-on-black? I'll take that over amber-on-black any day even if amber is apparently better for CRT burn-in.
                                                                                        • mst 11 months ago
                                                                                          I tend to use green on black for daytime hacking and amber on black in the evenings to reduce blue light issues.

                                                                                          But I also habitually run with jacked up contrast and nerfed brightness, so I make no claims that what works for me would be a good idea for anybody else.

                                                                                          • rk06 11 months ago
                                                                                            It is jarring when you use light mode. There is also issue with contrast. The red const gave me enough pain that I switched to reader view
                                                                                            • classified 11 months ago
                                                                                              If you have to ask that, please don't make dark-mode pages.
                                                                                          • your-username 11 months ago
                                                                                            [flagged]
                                                                                            • montyanderson 11 months ago
                                                                                              i like your site! seems like zig is really taking off.