JdeBP 3 days ago

I wrote up the iterator-driven for back in 2011, because it was one of those things that had been long-since forgotten about; along with what it would look like were it to be incorporated into the (then) C++ standard.

I am fortunate enough to own a copy of the High C/C++ Language Reference in English. (-:

* http://jdebp.uk./FGA/metaware-iterator-driven-for.html

* http://jdebp.uk./Proposals/metaware-iterator-driven-for.html

  • jkcxn 3 days ago

    Do you know how the break/return would get compiled down to? Would the yield function need to be transformed to return a status code and checked at the callsite?

    • JdeBP 3 days ago

      It's a non-local goto, also a MetaWare language extension, out of the anonymous nested function that the for statement body becomes to (effectively) an anonymous label right after the for statement.

      Another part of the High C/C++ Language Reference describes non-local labels and jumps to them. It doesn't go into great detail, but it does talk about stack unwinding, so expect something similar to how High C/C++ implemented throwing exceptions.

    • torginus 3 days ago

      Not sure, but imo you could do it with basically reversing the call/return mechanism - that is, whenever the iterator function returns, it saves its state to the stack, just like if it would during a function call, and conversely, when the outside context hands back the control to the iterator, it would restore its state, analogous to how a return from an outside context would work.

      • JdeBP 3 days ago

        That's not at all how MetaWare implemented iterator-driven for, though.

        As Joe Groff said in the headlined post, MetaWare implemented it by turning the nested body of the for statement into an anonymous nested function, which is called back (through a "full function pointer") from the iterator function whenever there's a "yield()" in that latter.

        So there's no "whenever the iterator function returns". It only returns when it has finished. The body of the for statement is called by and returns to the iterator function, which is in its turn called by and returns to the function that the for statement is in.

        All of the "saving state to the stack" that happens is just the quite normal mechanics of function calling, with merely some special mechanics to pass around a pointer to the lexically outer function's activation record (which is why a "full function pointer" is not a plain "function pointer") as a hidden parameter so that the (anonymous) lexically inner function knows where the outer one's automatic storage duration variables are.

        MetaWare also had non-local goto from within nested functions back out into lexically enclosing scopes, and since the for statement body is a nested function, it's just a question of employing that already available implementation mechanism (which in turn does the same sorts of things as throwing exceptions does, unwinding the stack through the iterator function) for break/continue/return (and of course goto) inside the for body.

  • Y_Y 3 days ago

    Is that supposed to be an upside-down smiley face?

WalterBright 3 days ago

D (also in Das BetterC) has:

1. underscores in literals:

    int a = 1_234_567;
2. case ranges:

    case 5 .. case 6:
3. named arguments:

    void test(int a, int b);

    void foo() { test(b:3, a:4); }
4. nested functions:

    int foo(int i) {
      int plus(int a) { return a + i; }
      return plus(3);
    }
5. static nested functions:

    int foo(int i) {
      static int plus(int a) { return a + i; }
      return plus(3);
    }

    Error: `static` function `test.foo.plus` cannot access variable `i` in frame of function `test.foo`
6. a feature similar to generators https://dlang.org/spec/statement.html#foreach_over_struct_an...
  • nialv7 3 days ago

    I was thinking about D the whole way while reading this. I just know I am going to see Walter Bright in the comments XD.

    • hermanhermitage 3 days ago

      Every time Walter posts it reminds me my dream language would simply be C with https://www.digitalmars.com/articles/C-biggest-mistake.html and probably go lang style interfaces. Maybe a little less UB and some extensions for memory safety proofs.

      • WalterBright 3 days ago

        That's why DasBetterC has done very well! You could call it C with array bounds checking.

        I occasionally look at statistics on the sources of bugs and security problems in released software. Array bounds overflows far and away are the top cause.

        Why aren't people just sick of array overflows? In the latest C and C++ versions, all kinds of new features are trumpeted, but again no progress on array overflows.

        I can confidently say that in the 2 decades of D in production use, the incidence of array overflows has dropped to essentially zero. (To trigger a runtime array overflow, you have to write @system code and throw a compiler switch.)

        The solution for C I proposed is backwards compatible, and does not make existing code slower.

        It would be the greatest feature added to C, singularly worth more than all the other stuff in C23.

        • pjmlp 3 days ago

          I don't even understand how the flip from having C++ collection frameworks being bounds checked by default (Turbo Vision, BIDS, OWL, MFC, Powerplant,...) ended happing, with C++98 getting a standard library that does exactly the opposite by default, and a strong cultural resistance on WG21 to change it until goverments started talking about security liabilities and what programming languages to accept in public projects.

          As for WG14, I have no hope, they ignored several proposals, and seem keen in having C being as safe as hand writing Assembly code, and even then, Assembly tends to be safer, as UB only happens when doing something the CPU did not expect, macro assemblers don't do clever optimizations.

          • pkphilip 2 days ago

            Wow, you mentioned Turbo Vision, OWL etc.. what a blast from the past! had fun developing applications using them

        • bakul 3 days ago

          > The solution for C I proposed is backwards compatible, and does not make existing code slower.

          Where can I read about it? The only way to make ptrs to array elements also safe that I can think of, is to replace them with triples: (base, element ptr, limit).

          • WalterBright 3 days ago
            • bakul 3 days ago

              Thanks. I got interested in this topic as people are talking about writing OS kernel code in Rust but a) it only helps new code and b) very hard to justify rewriting millions of lines of C code in Rust (plus rewrites are never 100% faithful feature wise). If on the other hand if C can be made safer, may be through a stepwise process where the code is rewritten incrementally to pass through C->C0->C1->Cn compilers, each making incremental language changes, much more of code can be made safer. It will never be as good as Rust but I do think this space is worth exploring.

              • uecker 2 days ago

                I would much prefer a safe C to Rust.

        • actionfromafar 3 days ago

          I don’t always agree but I’ll join you on this particular hill!

      • akira2501 3 days ago

        When writing software I almost never find myself in a situation where UB is a design concern or needs to be factored around in the structure.

        I almost always find myself struggling to name and namespace things correctly for long term durability. Almost all compiled languages get this wrong. They generally force you to consider this before you start writing code so you can explore the shape of the solution first.

        I think lisp is the only language I've used where this wasn't a burden, but in reality, lisp then forces you to deeply ponder your data structures and access ideology first, so I didn't find it to be that rewarding in the long run.

        I love that Go lets you bang simple "method like functions" straight onto the type. This solves the first layer of namespace problems. It does nothing for the second though, and in fact makes it worse, by applying "style guidelines" to the names of the containing types. I am constantly let down by this when writing Go code and I find it hard to write "good looking" code in the language which is all the more frustrating because this was what the guidelines were supposed to solve in the first place.

        I really just want C and I want to namespace my functions that act on structs into the structs themselves. Then I can name stuff however I want and I don't have to prefix_every_single_function() just so the me and the assembler can fully agree on the unmangled symbol table name which I will almost certainly never care about in 99% of what I compile.

        There's a real joy to the fast initial development and easy refactoring you can find in scripting languages. Too bad they all have wacky C interfaces and are slower than molasses.

        • jefftime 3 days ago

          If you haven't already I'd check out Zig. It does what you're describing if I am understanding correctly. There are some choices in that language I find annoying, but maybe you'll still enjoy it

    • amy-petrik-214 3 days ago

      I was thinking D but also there is basically a superset of D and HighC that incorporates best of both worlds, some people here may know it, it's called "Holy C" and it was the basis for a new operating system in the same way C was tightly integrated with UNIX. Written by Saint Terry

  • slaymaker1907 2 days ago

    I also think that the GC of D is really a nice feature. Sometimes you want manual memory management in low level code, but there is also a ton of stuff where it doesn't really matter and a GC just makes things easier. For example, if you're writing an in-memory cache service, you really don't want the cache entries themselves to be tracked by the GC since the GC is often unaware of the actual access patterns and just gets in the way, but most of the other components of that service are better served with a GC.

    • WalterBright 2 days ago

      On unexpected nice feature of the GC is it makes compile time function execution easy, as the engine for it doesn't have to emulate your custom storage allocator.

  • Gibbon1 3 days ago

    Questions?

    You got any idea why people hate the concept of nested functions in C?

    Why named named arguments test( a:4, b:3) instead of test(.a=4, b.=3);

    Have any idea how you would handle first class types in C.

    • WalterBright 3 days ago

      > You got any idea why people hate the concept of nested functions in C?

      No, I was surprised to hear it.

      > Why named named arguments test( a:4, b:3) instead of test(.a=4, b.=3);

      Consistency with how static initializers work in D.

    • kloop 3 days ago

      > You got any idea why people hate the concept of nested functions in C?

      It breaks the model of c mapping to the hardware.

      Functions in C are just labels you jump to. Assuming you want to capture values from the environment you're defining the function in, you have to implement some sort of function struct that is callable, and it probably won't work with any shared libraries on your system. Also it would be slightly slower, and C programmers tend to not be willing to make that tradeoff

      • WalterBright 2 days ago

        D (and Pascal) implement it by adding and additional parameter to the function called the "static link". (The stack pointer is the "dynamic link".) The static link points to the stack frame of the function it is lexically nested inside. The compiler accesses the variables by offsets to the static link.

    • pkphilip 2 days ago

      > Why named named arguments test( a:4, b:3) instead of test(.a=4, b.=3);

      Why do you consider the second one better than the first (a:4, b:3)?

      • lsllc 2 days ago

        It (the 2nd one) mirrors C's designated initializers for structures:

          struct foo f = {
              .bar = 1,
              .baz = 2,
          };
hgs3 3 days ago

Related: The 'lcc-win' C compiler added operator overloading, default function arguments, and function overloading (see "generic functions") [1]. The Plan 9 C compiler introduced several language extensions, some of which, like anonymous structs/unions would eventually be incorporated into the C standard. Present day GCC accepts the -fplan9-extensions flag [2] which enables some nifty features, like automatically converting a struct pointer to an anonymous field for function calls and assignments.

[1] https://lcc-win32.services.net/C-Tutorial.pdf

[2] https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html

bhouston 3 days ago

Who was the genius behind these features? Someone was incredibly forward looking in that company. Too bad it never got out into the world and impacted the language standards. It is surprising to see that so long ago.

Also previously covered here on Hacker News: https://news.ycombinator.com/item?id=38938402

Is there a PDF copy of this somewhere?

  • layer8 3 days ago

    CLU had for loops with iterators (generators) and yield in the mid–late 1970s [0]. The Icon programming language around the same time had similar generator features [1] (with yield spelled “suspend”). Ada (1983) also had such features I believe. These weren’t completely unknown language features.

    [0] https://publications.csail.mit.edu/lcs/pubs/pdf/MIT-LCS-TR-2...

    [1] https://dl.acm.org/doi/pdf/10.1145/800055.802034

    • Agingcoder 3 days ago

      Ada didn’t have generators back then and still doesn’t ( I think its being considered for inclusion soon ). But it had all the other features.

      • LiamPowell 3 days ago

        A task type with an entry is effectively a generator.

  • dfawcus 3 days ago

    Bitsavers has a copy of the HC 1.2 reference manual (1985)

    It describes underscores in numbers, case ranges, named parameters, nested functions, and full function variables.

        https://bitsavers.org/pdf/metaware/High_C_Language_Reference_Manual_1.2_Nov85.pdf
    
    See Appendix A - 50 odd pages from the end of the file.
  • jmspring 3 days ago

    Metaware was a prolific compiler company based out of Santa Cruz in the 80s/90s. Loved what they did, they also had a very interesting culture. I knew about them through cough shady cough sites when learning and writing code back in the day.

  • pjmlp 3 days ago

    Not really, if you dig into archives of high level programming languages since FORTRAN, Lisp, ALGOL and COBOL sprung into existence, you will see lots of these language ideas.

    You will also discover the rich history of systems programming languages, and how similar C and Go design's are in ignoring what was being done in other ecosystems, and past experiences.

JoshTriplett 3 days ago

For anyone wondering why the string literals in the pictured examples end with ¥n rather than \n, it looks like these code examples were written in Shift-JIS, and Shift-JIS puts ¥ where ASCII has \.

  • zzo38computer 3 days ago

    The problem with that is that the ASCII code for a backslash is also used as the second byte in a 2 byte character in Shift-JIS, which can sometimes cause Japanese string literals to not work properly in C. EUC-JP is better for this purpose, because it does not have that problem. (Using Shift-JIS with Pascal also does not have this problem, if you are using the (* *) comments instead of the { } comments.)

  • Eduard 3 days ago

    The author didn't provide information about when this book came out, nor is there any information to find on it. But I think at the book's release, Shift-JIS (the standard) didn't exist yet.

    Rather JIS X 0201 ( https://en.m.wikipedia.org.org/wiki/JIS_X_0201 ) was used, on which Shift-JIS is based.

  • nazgulsenpai 3 days ago

    Similarly, Japanese DOS prompts are C:¥ instead of C:\

Sad90sNerd 3 days ago

These extensions are Ada features.

Ada has:

- Labels in the form of: Call (Param_A => 1, Param_B => "Foo");

- Underscores can be used in numbers of any base: X : Integer := 1_000;

- Nested subprograms

- Range based tests

  • LiamPowell 3 days ago

    As mentioned in the article, Pascal had these even before Ada, and a task type with an entry is effectively a generator. I think people often forget that C was incredibly primitive for its time compared to multiple other languages.

    • pjmlp 3 days ago

      Many are sold on the story of C's greatness design, without bothering with historical fact checking.

      C rode the wave of UNIX's success, it is like someone getting into computers in 2024 and praising Javascript's greatness for Web development.

      ESPOL in 1961 already had unsafe code blocks, almost a decade before C came to be.

lexicality 3 days ago

Content aside, I'm fascinated by the typography in this book. It's simultaneously beautiful and horrendous!

I don't know enough about Japanese orthography or keming rules to be sure but it looks very much like they took a variable width font with both kanji and latin characters and then hard formatted it into fixed width cells?

Either way, it's nice that the code examples aren't in 8pt font like a lot of the books I have...

omoikane 3 days ago

This seems way ahead of its time, especially with generators. Maybe Fujitsu was able to just do it because they didn't bother with any of the lengthy standardization processes, but that's probably also why all these extensions seemed relatively unknown and had to be rediscovered and reinvented in modern C/C++ decades later.

  • JdeBP 3 days ago

    It was not Fujitsu. It was MetaWare, which had a fair degree of experience with compilers. It had a contemporaneous Pascal compiler, which was quite well known, and Pascal already had nested functions.

  • ahoka 3 days ago

    C could have been a much nicer language if it wasn’t captured by people who insisted that not even two’s complement can be in the standard.

    • dxuh 3 days ago

      I guess those people were parts of the embedded software industry before 2000 (maybe today, I don't know). It's a very good thing that C, the lingua franca of modern computing, actually runs on everything and not just on the stuff we use to browse the internet.

    • kevin_thibedeau 3 days ago

      There was a lot more innovation in computer architectures in the 80s and 90s. C89 is designed to permit implementation on unconventional hardware like the Lisp machine. C's flexible targeting is its greatest asset.

      • amszmidt 2 days ago

        Cs ability to run on "unconventional" (though there is little unconventional about the Lisp Machine, it is a just a stack based machine) long predates C89 (specifically).

        ZETA-C, a C compiler specific to the Lisp Machine, was already fully fledged by 1987 or there about. Don't have notes on when ZETA-C came to be, but it was much earlier than that, e.g., some of the headers are dated 1984.

        One cool thing about ZETA-C was you could embed Lisp code in between C code:

            extern FILE *stdin, *stdout, *stderr;
            #lisp
              ;; We don't want this file to "own" these
             (zeta-c:zclib>initialize-file-pointer |stdin| 0)
             (zeta-c:zclib>initialize-file-pointer |stdout| 1)
             (zeta-c:zclib>initialize-file-pointer |stderr| 2)
            #endlisp
      • Gibbon1 3 days ago

        Yep right when everyone

           1. Standardized on two's complement.
           2. Little endian.
           3. We went from word based memory systems to line based ones.
           4. RISC lost out to super scalar designs.
    • int_19h 3 days ago

      I don't think it was unreasonable at the time ANSI did the standardization originally. As with Common Lisp, they had to consider the numerous already-existing implementations and code written with them in mind.

    • saagarjha 2 days ago

      Two's complement being in the standard is overrated and for the most part doesn't really make much about the language better.

  • int_19h 3 days ago

    Coroutines and generators were already well-understood then (see Icon!), so I think it is indeed mostly about not having to worry about standardization.

AdmiralAsshat 3 days ago

Question: Was the book from the screenshots composed in Japanese, or composed in English and then translated into Japanese?

Since it's apparently from Fujitsu, I could see it being the former, but if so, I'm impressed with the quality of the English in the printf statements and code comments from non-native English speakers.

  • steinuil 3 days ago

    The engineers who wrote the High C compiler must have been able to read and write english well enough to have read documentation and source code for existing compilers, and I would imagine that this book was written by the creators of the language.

    • mintplant 3 days ago

      MetaWare was based in Santa Cruz, CA.

  • layer8 3 days ago

    It’s funny they’re using different fonts within the string literals.

pjmlp 3 days ago

Old memories, I had access to the MS-DOS version of it, still preferred Borland though.

dsp_person 3 days ago

Are there are good unofficial gcc plugins/extensions out there? It would be cool to extend C with a thing or two without adopting a full blown compiler like C2 or C3.

notorandit 3 days ago

I for one think that we have lost a number of good opportunities to make C language a better and .ore powerful one.

IMHO what I would need with C is a powerful pre-processor like jinja2 and some symbol manipulation features too.

  • pjmlp 3 days ago

    Including having proper slices, but not even one of the language authors was able to change WG14 mind on the matter.

    That is what happens to languages that leave their authors behind and embrace design by committee.

    • notorandit 3 days ago

      Yes, but the language itself is OK-ish besides a pre-processor that's too skinny.

      Standardizing libraries is one thing I really favor. But force fitting them into the language itself maybe like over-engineering.

      Let me have an example. qsort() and bsearch() are powerful but the call/return overhead is really unbearable. A (much) more powerful pre-processor which is really part of the language (and maybe syntax-aware) could help in creating templates that would generate solid code for either function with the bare minimum overhead.

      I am not saying like C++ templates, but like C++ templates.

zzo38computer 3 days ago

The file does not display because the browser insists on percent-encoding the apostrophe but the server insists that the apostrophe should not be percent-encoded, therefore resulting in an error message that it won't redirect properly. I can download the file properly with curl, though.

I think these are good ideas.

- Underscores in numeric literals: I think it is a good idea and is also what I had wanted to do before, too. (It should be allowed in hexadecimal as well as decimal)

- Case ranges: GNU C has this feature, too.

- Named arguments: This is possible with GNU C, although it doesn't work without writing it to handle this (although you can use macros to allow it to work with existing functions). You can pass a structure, either directly to the function, or using a macro containing a ({ }) block which extracts the values from the structure and passes them to the function (the compiler will hopefully optimize out this block and just pass the values directly). You can then use the named initialization syntax (which also allows arguments without named), and GNU C also allows you to have duplicates in which case only one of them will work, which allows you to use macros to provide default values. (I have tested this and it works.)

- Nested functions: GNU C also has it, but does not have the "full function value" like this one does, and I think it might be helpful. Nonlocal exits can also be helpful. (I also think the GNU's nested functions could be improved, by allowing them to be declared as "static" and/or "register" in order to avoid the need of trampoline implementations, although "static" and "register" would both have their own additional restrictions; "static" can't access local variables and functions from the function it is contained in unless they are also declared as "static", and "register" means the address can't be taken (therefore allowing the compiler to pass the local variables as arguments to the nested function).)

- Generator functions: I like this too and I think that it is useful (I had wanted things like this before, too). It is also interesting how it can work well with the nested functions.

There are some other things that I also think should be added into a C compiler (in addition to existing GNU extensions), such as:

- Allowing structures to contain members declared as "static". This is a global value whose name is scoped to the strucure within the file being compiled (so, like anything else declared as static, the name is not exported), so any accesses will access the single shared value. Even in the case of e.g. (x->y) if y is a static member then x does not need to be dereferenced so it is OK if it is a null pointer.

- Scoped macros, which work after the preprocessor works. It may be scoped to a function, a {} block inside of a function, a file, a structure, etc. The macro is only expanded where that name is in scope, and not in contexts where a new name is expected (e.g. the name of a variable or argument being declared) (in this case the macro is no longer in scope).

- Allow defining aliases. The name being aliased can be any sequence of bytes (that is valid as a name on the target computer), even if it is not otherwise valid in C (e.g. due to being a reserved word). Any static declaration that does not declare the value may declare the alias.

- Compile-time execution (with explicit declaration).

- Custom output sections, which can be used or moved into standard sections in a portable way. These sections might not even be mapped, and may have assertions, alignment, overlapping, etc.

- Allow functions to be declared as "register". If a function is declared as "static register" (so that the name is not exported), then the compiler is allowed to change the calling convention to work better with the rest of the program.

mananaysiempre 3 days ago

As a side note, these days you can fake named arguments in C, if you’re OK with every argument being 0 by default:

  // Declaration:
  void plot(float xlo, float xhi, float ylo, float yhi, float xinc, float yinc);
  struct plot_a { float xlo, xhi, ylo, yhi, xinc, yinc; };
  static inline void plot_i(struct plot_a _a) {
      // inline thunk to allow arguments to be passed in registers
      plot(_a.xlo, _a.xhi, _a.ylo, _a.yho, _a.xinc, _a.yinc);
  }
  #define plot(...) (plot_i((struct plot_a){ __VA_ARGS__ }))

  // Call:
  plot(alo, ahi, blo*2.0, bhi*2.0, .yinc = y, .xinc = f(x+z));
  • ori_b 3 days ago

    Note: this breaks if you want to pass struct literals:

       plot((myfoo){x,y})
    
    Macros will take the struct literals as multiple parameters:

        plot(
          .arg0=(myfoo){x,
          .arg1=y}
        )
    
    C macros are best left unused when possible.
    • mananaysiempre 3 days ago

      Nope! In general, that can be a problem, but not for this specific technique:

        $ cpp -P
        void plot(float xlo, float xhi, float ylo, float yhi, float xinc, float yinc);
        struct plot_a { float xlo, xhi, ylo, yhi, xinc, yinc; };
        static inline void plot_i(struct plot_a _a) {
            // inline thunk to allow arguments to go in registers
            plot(_a.xlo, _a.xhi, _a.ylo, _a.yho, _a.xinc, _a.yinc);
        }
        #define plot(...) (plot_i((struct plot_a){ __VA_ARGS__ }))
        
        plot((myfoo){x,y})
        plot(.yinc=(myfoo){x,y})
        ^D
        [...]
        (plot_i((struct plot_a){ (myfoo){x,y} }))
        (plot_i((struct plot_a){ .yinc=(myfoo){x,y} }))
      
      You could argue this is excessively clever, but when you need it, you really need it, so it could deserve known idiom status in the right situation.
      • ori_b 2 days ago

        > You could argue this is excessively clever, but when you need it, you really need it

        I've probably written millions of lines of C so far, and I don't think I have ever needed it.

        • mananaysiempre 2 days ago

          The usual workarounds are a stateful API (e.g. Cairo, OpenGL, or Windows GDI), passing a structure explicitly (oodles of examples in Win32, e.g. RegisterClass or GetOpenFileName), or a twiddling an object that’s actually just a structure dressed up in accessor methods (IOpenFileDialog).

          There could be reasons to use one of those still (e.g. extensibility while keeping a compatible ABI, as in setsockopt, pthread_attr_*, or arguably posix_spawnattr_*). But sometimes you really do need a finite, well-known but just plain large number of parameters that mostly have reasonable defaults. Old-style 2D APIs to draw and/or stroke a shape (or even just a rectangle) are the classic example. Plotting libraries (in all languages) are also prone to this. It does seem like these situations are mostly endemic to specific application areas—graphics of all kinds first of all—but that doesn’t make them not exist.

          If you don’t want to use function-like macros for anything ever even if this particular one works, that’s a valid position. But it does work, it does solve a real problem, and it is less awkward at the use site than the alternatives.

          • ori_b 2 days ago

            With large numbers of parameters, it's almost always more readable to use a config struct. Especially since often, you want to collect configuration from multiple sources, and incrementally initializing a struct that way is helpful.

    • CamperBob2 3 days ago

      C macros are best left unused when possible.

      Blame the committee for failing to specify an obvious and widely-demanded feature like named parameters.

      The only explanation is that the people in charge of the language don't write much code.

      • jimbob45 3 days ago

        There are a lot of syntactic sugar improvements the committee could make that they simply refuse to. Named parameters and function pointer syntax are compile-time fixes that would have zero runtime costs, yet it's 2024 and we've hardly budged from ANSI C.

        • CamperBob2 3 days ago

          Exactly. I actually think default parameters are hazardous without named-parameter support. When they added one, IMO they should have added the other as well, so that you can specify exactly which non-default parameters you're passing.

          • kevin_thibedeau 3 days ago

            I think this is more an appeasement of the C++ committee because they don't like the order of evaluation to be ambiguous when constructors with side effects come into play. Witness how they completely gimped the primary utility of designated initializers with the requirement to have the fields in order.

      • szundi 3 days ago

        Or they do and don’t want to learn stuff as “everything can be done the old way anyway”

    • szundi 3 days ago

      Say that to the Linux kernel or any embedded system

      • ori_b 3 days ago

        I've written both kernel code and embedded systems. It's easier to maintain the code when the preprocessor is avoided.

  • rramadass 3 days ago

    Pretty Neat.

    Have a link to any articles/sites/books which has a compendium of more tricks ?

amszmidt 3 days ago

So ... GCC has had some of these for ages.

    Pascal lets you match a range of values with case low..high; wouldn't it be great if C had that feature? High C does, another feature standard C and C++ never adopted.

https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html

    Nested functions

https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html

    Generators

GCC doesn't do those --- looks like a fun feature though!

My favourite, which was sadly removed was doing:

    foo ? zork : bork = 123;
Oh well...
  • JdeBP 3 days ago

    Not "ages" in comparison to how long MetaWare had them. High C had this stuff back in the early 1990s and 1980s.

    The headlined article doesn't mention it, but High C/C++ had modules all of those years ago, too. Anybase literals, as well. Tom Pennello participated in the standardization efforts back then, too, but none of this stuff made it in.

  • pistoleer 3 days ago

    You can still do

        *(foo ? &zork : &bork) = 123;
    • rescbr 3 days ago

      What a cursed thing. I like it.

  • ronsor 3 days ago

        > foo ? zork : bork = 123;
    
    That's kind of horrifying.
  • adastra22 3 days ago

    GCC’s different implementation is mentioned in the article.

  • qalmakka 3 days ago

    GCC nested functions are atrocious and deserve being banned from existence. Like the article rightfully says they've been implemented using weird hacks that make basically impossible to use them safely. There's a reason why Clang has categorically refused to implement them.

    • uecker 2 days ago

      I actually like GCC's nested functions a lot, although an implementation with function descriptors would be much better.