[HN Gopher] Obvious things C should do
       ___________________________________________________________________
        
       Obvious things C should do
        
       Author : LorenDB
       Score  : 239 points
       Date   : 2025-01-11 22:58 UTC (1 days ago)
        
 (HTM) web link (www.digitalmars.com)
 (TXT) w3m dump (www.digitalmars.com)
        
       | acheong08 wrote:
       | It really reads like the author just wants Zig.
        
         | convolvatron wrote:
         | or maybe even D
        
         | WalterBright wrote:
         | Zig copies features from D!
        
           | koolba wrote:
           | Imitation is the sincerest form of flattery.
        
             | WalterBright wrote:
             | Yup. D is the source for a number of recent features in
             | other languages.
             | 
             | The reason I embarked on D is because C and C++ were too
             | reluctant to move forward.
        
               | agumonkey wrote:
               | Do you think you will keep moving forward for the next
               | decade or will you merge when c/cpp becomes similar
               | enough to D ? Maybe your group still has tons of ideas
               | that need their own space to grow.
        
               | WalterBright wrote:
               | The ideas for advancing D come thick and fast. C and C++
               | will never merge with D, because we have different
               | philosophies of what makes for a great programming
               | language.
               | 
               | For example, D will never have a preprocessor. Or over my
               | dead body :-/
        
               | koolba wrote:
               | > For example, D will never have a preprocessor. Or over
               | my dead body :-/
               | 
               | Indeed. Compile time evaluation combined with a
               | preprocessor would make for some serious head scratching
               | when it comes to trusting trust.
        
               | agumonkey wrote:
               | Thanks a lot for your answer, I have another one, what
               | languages or topics do you follow for inspiration
               | regarding concepts ?
        
               | WalterBright wrote:
               | I could spent 500% of my time studying other languages.
               | So I don't, but the D user community isn't shy about
               | doing that for me!
        
               | bluGill wrote:
               | C++ is trying to eliminate the use of preprocessor. they
               | can't get rid of it but you shouldn't use it.
        
               | KerrAvon wrote:
               | Stroustrup has been saying that in print since 1993, and
               | it hasn't happened yet.
        
               | WalterBright wrote:
               | I see revision after revision of Standard C++, and no
               | progress in that direction. I (along with Andrei
               | Alexandrescu and Herb Sutter) made a proposal to add D's
               | _static if_ feature to eliminate the need for _#if_. It
               | was vehemently rejected.
        
               | ksec wrote:
               | >The reason I embarked on D is because C and C++ were too
               | reluctant to move forward.
               | 
               | I just want to take this opportunity to say thank you.
               | While D may not taken up from the rest of the world. It
               | has surely lived on in C, C++ and many other languages.
               | Still wish more people would use Das C.
        
               | WalterBright wrote:
               | Yes, the influence is there.
               | 
               | For example, C++ pivoted to using ranges instead of
               | iterators, and even C# changed their iterators to be like
               | D's ranges (or so I've been told).
        
               | WhereIsTheTruth wrote:
               | I'm curious, is there any features, from any language,
               | that you wish you had implemented, or want to copy, in D?
        
               | WalterBright wrote:
               | There's active work going on to add pattern matching.
               | Just recently I completed the addition of move
               | constructors. They are different from C++'s, and I bet
               | they'll turn out to be much better.
        
             | ksec wrote:
             | Identity theft is not a joke, millions of families suffer
             | every year!
             | 
             | Michael!
        
           | throwawaymaths wrote:
           | more importantly, though, zig deliberately doesnt implement a
           | whole TON of things that D does.
           | 
           | sometimes parsimony is called for. zig is basically c--+
           | where the + is the constexpr stuff.
        
             | WalterBright wrote:
             | It keeps adding D features anyway, like constexpr.
        
               | throwawaymaths wrote:
               | the constexpr stuff has been around since the
               | beginning[0]. concretely, what other thing from D do you
               | claim zig has added since?
               | 
               | [0] minimally 2019, 3 years in:
               | 
               | https://youtu.be/Gv2I7qTux7g?si=p0kVhtB56GvVLLNr
        
               | WalterBright wrote:
               | D had compile time execution of functions in 2007.
               | 
               | Order-independent top level declarations.
               | 
               | Underscores embedded in integer literals. (I stole this
               | idea from Ada, which had been forgotten. Soon after D
               | popularized it, it became standard in other languages.)
               | 
               | Continue or break to labeled loop.
               | 
               | Fixed sizes for ints, longs, etc.
               | 
               | Of course, I don't know if there's a straight line here,
               | and Zig is welcome to use any features from D that they
               | like. But it's just interesting that things innovated in
               | D pop up in subsequent designs.
        
               | UncleEntity wrote:
               | >> Continue or break to labeled loop.
               | 
               | Hasn't Java had that since the beginning?
               | 
               |  _rummaging around in my grammar folder..._
               | 
               | BreakStatement ::= "break" [ IDENTIFIER ] ';'
               | 
               | ContinueStatement ::= "continue" [ IDENTIFIER ] ';'
        
               | WalterBright wrote:
               | And so it does. I had forgotten. Thanks for the reminder!
        
               | sundarurfriend wrote:
               | > Underscores embedded in integer literals.
               | 
               | Perl had this years before D even existed [1]. Given its
               | earlier age and higher reach, it's likely that Perl did
               | more to popularize the idea as well.
               | 
               | > Continue or break to labeled loop.
               | 
               | Also a feature that Perl had years before D's existence
               | [2].
               | 
               | These two are the ones I immediately recognized because
               | of my familiarity with Perl, but given the trend, I'm
               | doubtful of the other claims as well now.
               | 
               | [1] https://perldoc.perl.org/5.005/perldata [2]
               | https://perldoc.perl.org/5.005/perlsyn#Loop-Control
        
               | WalterBright wrote:
               | I did not know Metaware and Perl did this, as I've never
               | used either. I know that my inspiration for it came from
               | Ada.
               | 
               | I accept that Metaware and Perl did this before D. But I
               | still claim that adoption of it in other languages came
               | shortly after I popularized it in D, as I included it in
               | many presentations about it.
        
               | dfawcus wrote:
               | Metaware High-C version 1.2 (Nov 1985) had underscores in
               | floating point and integer literals. Possibly it had that
               | even earlier.
               | 
               | Possibly also taken from Ada, as other text in that
               | section of the manual reference Ada.
               | 
               | See A.3 pg 169 (and 58+) of 235 in: https://bitsavers.org
               | /pdf/metaware/High_C_Language_Reference...
        
               | WalterBright wrote:
               | I did not know that. Thanks for pointing it out.
        
             | WhereIsTheTruth wrote:
             | Parsimony? more like thankful for learning from other
             | language experiments and research work
             | 
             | Zig is young, and far from 1.0, and many long promised
             | features still not implemented
        
               | throwawaymaths wrote:
               | > many long promised features still not implemented
               | 
               | name one that isn't async / sane recursion (which is also
               | async)
        
         | robterrell wrote:
         | The author is the creator of D, so he's probably fine with D.
         | And D is something like 25 years old. Whereas Zig is just a
         | toddler.
        
           | johnnyanmac wrote:
           | ahh, that explains the ImportC comparisons. Also didn't
           | realize that person is also a regular on HN as well.
        
       | TinkersW wrote:
       | C++ has all of these except forward referencing
       | declarations(though Importing Declarations requires modules which
       | nobody uses yet).
       | 
       | I'm not sure why forward reference declarations is needed
       | nowadays(or if it really is from a language standpoint).
       | 
       | C could probably copy C++'s constexpr & static_assert stuff to
       | get the first 2.
        
         | WalterBright wrote:
         | Nobody uses C++ modules because they are clumsy to use. D's are
         | easy. Note: anyone is free to copy D's module design. It's the
         | best one out there.
         | 
         | > I'm not sure why forward reference declarations is needed
         | nowadays
         | 
         | The article gives reasons. Although they aren't _necessary_ ,
         | they _are_ deleterious to code layout which becomes a slave to
         | the declaration order rather than aesthetic order.
         | 
         | > C++'s constexpr
         | 
         | is still lagging behind D's, after 17 years of development. In
         | D, the garbage collector makes memory allocation in it trivial.
         | Furthermore, only the _path_ taken through a function needs to
         | be CTFE-compatible, the path not taken does not.
        
           | zeroonetwothree wrote:
           | Robert Frost would approve
        
         | dccsillag wrote:
         | If I'm not mistaken, the treatment of forward declarations
         | proposed in the article actually breaks the C standard, which
         | would be a rather pressing concern. As far as I am aware, that
         | is _the_ reason why things are the way they are right now in C
         | land.
         | 
         | (In the past, there were more legitimate concerns on the ease
         | of implementation. Nowadays, as the article points out, they
         | are pretty moot, other than having to keep backwards-
         | compatibility.)
         | 
         | I'm also rather bothered that on the bit on const execution in
         | the article, there was no discussion on how to deal with
         | functions that may not terminate or take rather long to
         | execute. Especially considering the unit tests motivation, this
         | seems like a rather blaring omission.
        
           | WalterBright wrote:
           | How does it break the C Standard?
           | 
           | > how to deal with functions that may not terminate or take
           | rather long to execute
           | 
           | Control-C, the same as when running any executable that
           | shouldn't be taking that long. It doesn't solve the halting
           | problem :-/
        
             | UncleEntity wrote:
             | So a single typo DDoSes the entire Red Hat buildbot fleet?
        
               | kstenerud wrote:
               | You set a timeout, like in any robust CI.
        
         | loeg wrote:
         | C's already got static_assert; just missing constexpr.
        
       | chacham15 wrote:
       | While the author has WAY more knowledge/experience than me on
       | this and so I wonder how he would solve the following issues:
       | 
       | Evaluating Constant Expressions
       | 
       | - This seems really complicated...if you're working within a
       | translation unit, thats much simplified, but then you're much
       | more limited in what you can do without repeating a lot of code.
       | I wonder how the author solves this.
       | 
       | Compile Time Unit Tests
       | 
       | - This is already somewhat possible if you can express your test
       | as a macro, which if you add in the first point, then this
       | becomes trivial.
       | 
       | Forward Referencing of Declarations
       | 
       | - I think there may be a lot of backlash to this one. The main
       | argument against this is that it then changes the compiler from a
       | one-pass to two pass compiler which has its own performance
       | implications. Given the number of people who are trying to
       | compile massive codebases and go as far as parallelizing
       | compilation of translation units, this may be a tough pill for
       | them to swallow. (evaluating constant expressions probably comes
       | with a similar/worse performance hit caveat depending on how its
       | done)
       | 
       | Importing Declarations
       | 
       | - This is a breaking change...one of the ways I have kind of
       | implemented templating in C is by defining a variable and
       | importing a c file, changing the variable, and then reimporting
       | the same c file. Another thing I've done is define a bunch of
       | things and then import the SQLite C Amalgamation and then add
       | another function (I do this to expose a SQLite internal which
       | isnt exposed via its headers). All of these use cases would break
       | with this change.
       | 
       | Are there any thoughts about these issues? Any ways to solve them
       | perhaps?
        
         | WalterBright wrote:
         | > if you're working within a translation unit, thats much
         | simplified, but then you're much more limited in what you can
         | do without repeating a lot of code. I wonder how the author
         | solves this.
         | 
         | You are correct in that the source code to the function being
         | evaluated must be available to the compiler. This can be done
         | with #include. I do it in D with importing the modules with the
         | needed code.
         | 
         | > This is already somewhat possible if you can express your
         | test as a macro, which if you add in the first point, then this
         | becomes trivial.
         | 
         | Expressing the test as a macro doesn't work when you want to
         | test the function. The example I gave was trivial to make it
         | easy to understand. Actual use can be far more complex.
         | 
         | > Performance
         | 
         | D is faster at compiling than C compilers, mainly because:
         | 
         | 1. the C preprocessor is a hopeless pig with its required
         | multiple passes. I know, I implemented it from scratch multiple
         | times. The C preprocessor was an excellent design choice when
         | it was invented. Today it is a fossil. I'm still in awe of why
         | C++ has never gotten around to deprecating it.
         | 
         | 2. D uses import rather than #include. This is just way, way
         | faster, as the .h files don't need to be compiled over and over
         | and over and over and over ...
         | 
         | D's strategy is to separate the parse from the semantic
         | analysis. I suppose it is a hair slower, but it also doesn't
         | have to recompile the duplicate declarations and fold them into
         | one.
         | 
         | Compile time function execution can be a bottleneck, sure, but
         | that (of course) depends on how heavily it is used. I tend to
         | use it with a light touch and the performance is fine. If you
         | implement a compiler using it (as people have done!) it can be
         | slow.
         | 
         | > one of the ways I have kind of implemented templating in C is
         | by defining a variable and importing a c file, changing the
         | variable, and then reimporting the same c file. Another thing
         | I've done is define a bunch of things and then import the
         | SQLite C Amalgamation and then add another function (I do this
         | to expose a SQLite internal which isnt exposed via its
         | headers). All of these use cases would break with this change.
         | 
         | I am not suggesting removing #include for C. The import thing
         | would be additive.
         | 
         | > Are there any thoughts about these issues?
         | 
         | If you're using hacks to do templating in C, you've outgrown
         | the language and need a more powerful one. D has top shelf
         | metaprogramming - and as usual, other template languages are
         | following in D's path.
        
           | daymanstep wrote:
           | Can't you use precompiled headers?
        
             | WalterBright wrote:
             | Interesting you brought that up. I implemented them for
             | Symantec C and C++ back in the 90s.
             | 
             | I never want to do that again!
             | 
             | They are brittle and a maintenance nightmare. They did
             | speed up compilations, though, but did not provide any
             | semantic advantage.
             | 
             | With D I focused on fast compilation so much that
             | precompiled headers didn't offer enough speedup to make
             | them worth the agony.
        
               | fuhsnn wrote:
               | >They are brittle and a maintenance nightmare
               | 
               | I happened to be reading DMC source this week, those
               | hydrate/dehydrate stuff really is everywhere (which I
               | assume is solely used for precompiled headers?)
        
               | WalterBright wrote:
               | Yup. I spent a crazy amount of time debugging that. The
               | tiniest mistake was a big problem to find.
        
             | ndesaulniers wrote:
             | I had an intern try to use precompiled headers for the
             | Linux kernel. The road block they found was that the
             | command line parameters used to compile the header must
             | exactly match for all translation units which it is used.
             | This is no the case for the Linux kernel. We could compile
             | the header multiple times, but the build complexity was not
             | something we could overcome during the course of one
             | internship.
        
               | WalterBright wrote:
               | > must exactly match
               | 
               | Yup. My compiler kept a list of which switches would
               | perturb compilation and so would invalidate the
               | precompiled header, and which did not.
               | 
               | Precompiled headers are an awful, desperate feature. Good
               | riddance.
        
           | chacham15 wrote:
           | Thanks for taking the time to respond! I have a few followup
           | questions if thats ok:
           | 
           | > You are correct in that the source code to the function
           | being evaluated must be available to the compiler. This can
           | be done with #include. I do it in D with importing the
           | modules with the needed code.
           | 
           | > D's strategy is to separate the parse from the semantic
           | analysis. I suppose it is a hair slower, but it also doesn't
           | have to recompile the duplicate declarations and fold them
           | into one.
           | 
           | I dont quite follow all the implications that these
           | statements have. Does the compiler have a different way of
           | handling a translation unit?
           | 
           | - Is a translation unit the same as in C, but since you're
           | #including the file you would expect multiple compilations of
           | a re-included C file? woudnt this bloat the resulting
           | executable (/ bundle in case of a library)
           | 
           | - Are multiple translation units compiled at a time? Wouldnt
           | this mean that the entire translation dependency graph would
           | need to be simultaneously recompiled? Wouldnt this inhibit
           | parallelization? How would it handle recompilation? What
           | happens if a dependency is already compiled? Would it
           | recompile it?
           | 
           | > Performance
           | 
           | I think a lot of this is tied to my question about
           | compilation/translation units above, but from my past
           | experience we have "header hygene" which forces us to use
           | headers in a specific way, which if we do, we actually get
           | really good preprocessor performance (a simple example being:
           | dont use #include in a header), how would you compare
           | performance in these kinds of situations vs a compiler
           | without (i.e. either recompiled a full source file or looking
           | up definitions from a compiled source)?
           | 
           | > If you're using hacks to do templating in C, you've
           | outgrown the language and need a more powerful one. D has top
           | shelf metaprogramming - and as usual, other template
           | languages are following in D's path.
           | 
           | yes, as also demonstrated in the performance question, we do
           | a lot to work within the confines of what we have when other
           | tools would handle a lot more of the lifting for us and this
           | is a fair criticism, but on the flip side, I dont have the
           | power to make large decisions on an existing codebase like
           | "lets switch languages" (even if for a source file or
           | two...I've tried) as much as I wish I could, so I have to
           | work with what I have.
        
             | thayne wrote:
             | > Is a translation unit the same as in C, but since you're
             | #including the file you would expect multiple compilations
             | of a re-included C file? woudnt this bloat the resulting
             | executable (/ bundle in case of a library)
             | 
             | I think the idea is that compiling a translation unit
             | produces two outputs, the object code (as it currently
             | does), and an intermediate representation of the exported
             | declarations, that could be basically a generated .h file,
             | but it would probably be more efficient to use a different
             | format. Then dependent translation units use those
             | declaration files.
             | 
             | With this, you can still compile in parallel. You are
             | constrained by the order of dependencies, but that is
             | already kind of the case.
             | 
             | One complication is that ideally, if the signature doesn't
             | change, but the implementation does, you don't need to re-
             | compile dependent translation units. This is trivial if
             | your build system detects changes based on content (like,
             | say, bazel), but if it uses timestamps (like make) then the
             | compiler needs to ensure the timestamp isn't updated when
             | the declarations don't change.
             | 
             | But this really isn't a new concept. Basically every modern
             | compiled language works fine without needing separate
             | header files.
        
               | dwattttt wrote:
               | > This is trivial if your build system detects changes
               | based on content (like, say, bazel), but if it uses
               | timestamps (like make) then the compiler needs to ensure
               | the timestamp isn't updated when the declarations don't
               | change.
               | 
               | This is where the traditional distinction of "compiler vs
               | Make" makes things harder; you want dependencies tracked
               | at the "declaration" level, rather than the file level.
               | If the timestamp _and_ content of the exported
               | declarations file change, but none of the _used_
               | declarations changed, then there's no more compilation to
               | be done. At best with file level tracking your build
               | system will invoke the compiler for every downstream
               | dependency, and they can decide if there's any more work
               | to be done.
               | 
               | The build system would need to know which declarations
               | are used (and what a declaration is) to do better.
        
               | WalterBright wrote:
               | The D compiler has an option to generate a "header file"
               | from D modules. It's called a .di file. It's useful if
               | you want to hide the implementation from a compiler, as
               | you would with libraries.
               | 
               | As it turned out, though, people just found it too
               | convenient to just import the .d file.
               | 
               | But as a very unexpected dividend, it was _discovered_
               | that the D compiler would generate .di files from
               | compiling .c files, and realized that D had an inherent
               | ability to _translate_ C code to D code!!!! This has
               | become rather popular.
        
             | WalterBright wrote:
             | > I dont have the power to make large decisions on an
             | existing codebase like "lets switch languages"
             | 
             | We struggled with that for a long time with D. And finally
             | found a solution. D can compile Standard C source files and
             | make all the C declarations available to the D code. When I
             | proposed it, there was a lot of skepticism that this could
             | ever work. But when it was implemented and debugged, it's
             | been a huge win for D.
             | 
             | > Performance
             | 
             | With D you can put all your source files on one command
             | line invocation. That means that imports are only read
             | once, no matter how many times it is imported. This works
             | so well D users have generally abandoned the C approach of
             | compiling each file individually and then linking them
             | together. A vast amount of time is lost in C/C++
             | compilation with simply reading the .h files thousands of
             | times.
             | 
             | Modules/imports are a _gigantic_ productivity booster. They
             | 're not hard to implement, either. Except for the way C++
             | did it.
             | 
             | > re multiple translation units compiled at a time? Wouldnt
             | this mean that the entire translation dependency graph
             | would need to be simultaneously recompiled? Wouldnt this
             | inhibit parallelization? How would it handle recompilation?
             | What happens if a dependency is already compiled? Would it
             | recompile it?
             | 
             | Yes, yes, yes, yes. And yet, it _still_ compiles faster!
             | See what I wrote above about not needing to read the .h
             | files thousands of times. Oh, and building one large object
             | file is faster than building a hundred and having to link
             | them together.
        
               | ufo wrote:
               | I know that in other languages, one obstacle for "just
               | compile the C files" is that the target language might
               | not have pointers and thus have difficulty representing
               | things such as return-by-pointer.
               | 
               | I suppose in D this was less of an issue because D has
               | pointers?
        
               | WalterBright wrote:
               | I'm not sure what you mean.
        
               | ufo wrote:
               | A foreign function interface that's based on parsing C
               | files must translate C types and interfaces into types
               | and interfaces of the target language. I suppose it
               | helped that D's type system has many similarities with C,
               | including support for pointers.
               | 
               | (The issue with return-by-pointer is that in C it's
               | common to use the return value for an error code and use
               | pointer arguments to pass data back to the caller. These
               | are awkward to map to a target language that doesn't have
               | pointers)
        
           | baranul wrote:
           | Nice explanation. Modules are the way forward. Looks to
           | always have been. Not understanding the resistance, when the
           | advantages are clear.
        
             | WalterBright wrote:
             | I do understand the resistance. C is a simple, comfortable
             | language, and its adherents want it to stay that way, warts
             | and all.
             | 
             | But in the context of that, what baffles me is the
             | additions to the C Standard, such as useless (but
             | complicated!) things like normalized Unicode identifiers,
             | things with very marginal utility like generic functions,
             | etc. Why those and not forward declarations?
        
         | xigoi wrote:
         | I personally don't like forward referencing because it makes
         | code harder to read. You can no longer rely on the dependency
         | graph being in topological order.
        
           | WalterBright wrote:
           | As the article writes, that forces the private leaf functions
           | to be at the top, with the public interface at the end of the
           | file. The normal way is the public interface at the top, and
           | the implementation "below the fold", so to speak.
           | 
           | > topological order
           | 
           | You are correct. But its the reverse topological order, which
           | is not the most readable ordering. One doesn't read a
           | newspaper article starting at the bottom.
        
             | xigoi wrote:
             | Maybe it's because I'm primarily a mathematician, but I
             | like building complex stuff up from primitives and having
             | the most important results at the end.
        
               | WalterBright wrote:
               | Vive la difference - and you'll still be able to do it
               | your way!
        
               | kccqzy wrote:
               | That's not how I do things in math. I always need
               | motivation first. So I start with the theorem, look at a
               | couple of examples to see why this theorem is
               | interesting, and then the various lemmas leading into the
               | proof. So that means I really like declaring but not
               | defining the public interface first, and then define the
               | private helper functions, and finally definitions for the
               | public interface.
        
               | Lvl999Noob wrote:
               | Perhaps the difference is having algorithm in your head
               | and just putting it into code, versus only knowing the
               | top level work to be done and implementing the needed
               | operations later.
               | 
               | If I am writing some kind of service, I would write the
               | main public functions first, using undefined functions in
               | their bodies as needed. Then I would implement those
               | functions below.
        
               | bmacho wrote:
               | I think you mean it in the context of _proofs_ , right?
               | Proofs are indeed often best written in a topological
               | order: a series of true statements, where every reference
               | refer backwards.
               | 
               | You don't often see                   Answer = A + B,
               | where                   A = ...         ...         B =
               | ...
               | 
               | albeit you sometimes see it, and it is totally valid. For
               | proofreading something, it makes a big difference: if
               | things are in a topological order, you can simulate a
               | constant memory finite machine. If they are not in a
               | topological order, well, probably you better just rewrite
               | it (or at least I do).
               | 
               | For most other things, I usually prefer the bird-view
               | first, when I am doing or reading some elses math.
               | 
               | Funnily the language Haskell which operates on
               | definitions, is very order independent, it even allows
               | circular definitions. I like it for leetcode and such.
        
             | chikere232 wrote:
             | People learn the ordering. If that is their biggest hurdle
             | learning C they have a blessed life
        
         | billfruit wrote:
         | Every other language does seems to not require header
         | file/forward declarations. I don't understand the backlash
         | against that.
         | 
         | Are modern C compilers actually still single pass?
        
           | WalterBright wrote:
           | > Are modern C compilers actually still single pass?
           | 
           | All except ImportC, which effortlessly handles forward
           | references. (Mainly because ImportC hijacks the D front end
           | to do the semantics.)
        
           | UncleEntity wrote:
           | A bit of an aside but I was poking around in the SPIR-V spec
           | yesterday and they can do forward references because the call
           | site contains all the information to determine the function
           | parameter types. Just thought it was interesting and not
           | really something I had thought about before.
        
       | ryukoposting wrote:
       | Yeah... no.
       | 
       | Constexpr function evaluation sounds like a great idea until you
       | start trying to use it, and get surprised when seemingly-
       | constexpr-safe functions _aren 't_ constexpr. Or, you tweak one
       | function and suddenly all your fancy compile-time unit tests
       | explode.
       | 
       | Ok, so you get around that with good code hygiene and by limiting
       | the complexity of your constexpr functions... in other words, do
       | the exact things we already do with preprocessor macros.
       | 
       | Alternatively, you add a constexpr keyword to the lang, but now
       | we have red functions and blue functions. Great.
       | 
       | In another language, there'd still be an argument for the type-
       | safety that would precipitate from constexpr function eval, but
       | this is C we're talking about.
       | 
       | How about container_of? Could we please standardize that already?
       | Why is this crucial and immensely useful macro a thing we all
       | copy-paste from that one page on kernel.org?
        
         | WalterBright wrote:
         | Just like constant expressions, a function run at compile time
         | needs to be _pure_ , which means no globals, no system calls,
         | no I/O, no undefined behavior. Yes, that does constrain it
         | somewhat, but D users have found it to be immensely useful
         | anyway.
         | 
         | There's really no comparison with preprocessor macros. All the
         | preprocessor can do is trivial expressions with long values.
         | Not even floating point.
         | 
         | > you add a constexpr keyword to the lang, but now we have red
         | functions and blue functions
         | 
         | My proposal (and D) does not require constexpr. The same
         | function can be used at run or compile time. There is no need
         | for that keyword. C++ made a mistake.
        
           | ryukoposting wrote:
           | > D users have found it to be immensely useful anyway.
           | 
           | I think you're focused on something that D programmers found
           | helpful, rather than focusing squarely on the needs of C
           | programmers. C and D are both good languages. Their use cases
           | _can_ overlap, but frequently don 't.
           | 
           | >=70% of the code I write for work is C, as I'm an embedded
           | firmware dev. C meets the very particular needs of bare-metal
           | development, a use case that continues to be underserved by
           | Rust, Zig, and other supposed successors to C. So, I'll be
           | approaching this with a strong bias towards that perspective.
           | 
           | To me, the argument about unit testing rings hollow because
           | of all the other, far more complicated and runtime-subverting
           | things that would _also_ need to become standardized before
           | it would be feasible to unit test C programs without help
           | from the hideous hacks we use today, like CMock. So, all of
           | that isn 't doing anything for me.
           | 
           | > My proposal (and D) does not require constexpr. The same
           | function can be used at run or compile time.
           | 
           | Like you, I dislike the constexpr keyword. But, particularly
           | considering your own example of defining an enum value, I
           | don't see how the "implicit const-evaluatable" approach makes
           | my life easier. Calling functions to define an enum's value
           | is cute, but I can't tell you why I'd actually want to do
           | that. You mention that the preprocessor can't handle floats,
           | but, well, neither can enums!
           | 
           | Adding a single printf (or, in my case, kprintf or LOG_DBG or
           | whatever) would become liable to nuke some constant
           | evaluation happening somewhere far up an obscure call chain.
           | The basic reality of C is that you _will_ , at one point or
           | another, encounter a situation where your only debugging tool
           | is print statements (or a single LED). That's the cold
           | reality of C's paper-thin runtime.
           | 
           | So, I really dislike the idea of having a feature that's
           | liable to make your code go "boom" at compile time because
           | you put a print statement in just the wrong spot. Or a write
           | to a memory-mapped register. Or a hard jump into a blob
           | sitting somewhere in memory. Or inline assembly. Or a call to
           | a function that uses any of those things, even once. Even
           | without the keyword, you end up with red functions and blue
           | functions. It's just harder to tell which ones are which.
           | 
           | Defining some const floats? Sure, that's neat. If you could
           | use this feature to define huge matrices of floats, that'd be
           | pretty cool! There's just a boatload of gotchas.
        
             | WalterBright wrote:
             | > your own example of defining an enum value
             | 
             | That is hardly the only place that has a constant-
             | expression in the grammar. (BTW, D enums can also be
             | floats, and even string literals!) You could use CTFE to
             | initialize const floating point globals. static_assert also
             | takes a constant-expression.
             | 
             | > There's just a boatload of gotchas.
             | 
             | The D community has 17 years experience with it. It remains
             | an indispensable feature.
             | 
             | As for embedding a printf, that has come up. Recall
             | elsewhere I said that only the path through a function has
             | to be pure for CTFE to work, not the whole function?
             | int sum(int a, int b) {             int s = a + b;
             | if (!__ctfe) printf("sum is %d\n", s);             return
             | s;         }
             | 
             | __ctfe is a keyword that says "CTFE is executing this
             | function".
             | 
             | > If you could use this feature to define huge matrices of
             | floats, that'd be pretty cool!
             | 
             | I use it to statically initialize complicated tables at
             | compile time. Before CTFE, I wrote a separate executable
             | that would emit source code with the array initializer. I
             | like the new way mucho bettero.
             | 
             | If you prefer "hideous hacks" (your words!) I won't take
             | that away from you.
        
               | chikere232 wrote:
               | Not to take away from the possible usefulness of constant
               | functions, but it's amusing that the article example of
               | `sum(5,6)` would work great in C if written as 5 + 6, or
               | done as the equivalent macro
        
               | WalterBright wrote:
               | Trivial examples make it easy to explain and understand
               | the concept. Just like with Calculus, we don't start with
               | integrating _e^x_. We start with integrating _x_.
        
         | throw16180339 wrote:
         | If you're using C++ 20, you can mark functions _consteval_ to
         | force constant evaluation.
        
       | kreco wrote:
       | > Evaluating Constant Expressions
       | 
       | The examples are quite simple in the article but I believe more
       | complex cases would significantly degrade the compiler speed (and
       | probably the memory footprint as well) and would require a VM to
       | leverage this.
       | 
       | Which is probably assumed "too complex" to go into the standard.
       | I'm not saying it's impossible, but I kind of understand _why_
       | this would not go into any kind of standard.
       | 
       | > Importing Declarations
       | 
       | I wish C++ (or even C) would have gone into this direction
       | instead the weird mess of what is defined for C++20.
       | 
       | Additionally you might import module into some symbol, like:
       | #import "string.c" as str
       | 
       | and every non-static symbols from the file can be accessed from
       | like:                 str.trim(" Hello World ");
       | 
       | > __import dex;
       | 
       | This is totally tangential but I don't like when file paths are
       | not explicit. In this specific case I don't know if I'm importing
       | dex.d or dex.c.
        
         | WalterBright wrote:
         | > I kind of understand why this would not go into any kind of
         | standard.
         | 
         | Other popular languages can do it. That aside, it is an
         | immensely popular and useful feature in D.
         | 
         | And yes, as one would expect, the more it is used, there's
         | compile time speed and memory consumption required. As for a
         | VM, the constant folder is already a VM. This just extends it
         | to be able to handle function calls. C has simple semantics, so
         | it's not that bad.
         | 
         | > Additionally
         | 
         | Great minds think alike! Your suggestions are just what D
         | imports do. https://dlang.org/spec/module.html#import-
         | declaration
         | 
         | > In this specific case I don't know if I'm importing dex.d or
         | dex.c
         | 
         | This issue does come up. The answer is setting up your import
         | path. It's analogous to the C compiler include path.
        
         | loeg wrote:
         | > The examples are quite simple in the article but I believe
         | more complex cases would significantly degrade the compiler
         | speed (and probably the memory footprint as well) and would
         | require a VM to leverage this.
         | 
         | > Which is probably assumed "too complex" to go into the
         | standard. I'm not saying it's impossible, but I kind of
         | understand why this would not go into any kind of standard.
         | 
         | I mean, it's basically 1:1 with the constexpr feature in C++.
         | Almost every C compiler is already a C++ compiler, supporting
         | constexpr functions and evaluation in C can't be _that_ bad,
         | can it?
        
         | thayne wrote:
         | > I believe more complex cases would significantly degrade the
         | compiler speed (and probably the memory footprint as well) and
         | would require a VM to leverage this.
         | 
         | I'm pretty sure most production grade c compilers already do
         | some level of compiler time evaluation for optimization. And C
         | already has constant expressions.
         | 
         | I think a bigger hurdle would be that the compiler needs access
         | to the source code of the function, so it would probably be
         | restricted to functions in the same translation unit.
         | 
         | And then there is the possibly even bigger people problem of
         | getting a committee with representives from multiple compilers
         | to agree on the semantics of such constant evaluation.
        
       | foul wrote:
       | Not again, not another D thread noooooooooooooooooo (i'm joking)
        
         | WalterBright wrote:
         | All your base belong to D!
        
       | EuAndreh wrote:
       | Good suggestions, but also meh, e.g.: forward declaration
       | requirement enables a single-pass compiler to emit code on-the-
       | fly.
       | 
       | I have a much better list for things to add to C: Nothing. C
       | isn't perfect, or nearly as good as it could be, but simply
       | adding things onto C gets you C++.
       | 
       | Adjusting what sircmpwn says: in C you don't solve problems by
       | adding features, but by writing more code in C.
       | 
       | I liked an answer on stack overflow on a question on "how to
       | write a generic function wrapper in Go", or something similar.
       | Many suggestions included reflection, but the author wanted
       | something simpler with varargs without reflection. A comment
       | simply said: "wrong language".
       | 
       | I'd rather adopt this position for some languages, instead of add
       | more and more to C3X. I do away with things in C23, and don't
       | want even more things added in to C.
       | 
       | Making a strech of OP's arguments: "look at all this cool things
       | that C could do, and that D does!". Well, go on and use D,
       | nothing wrong with that.
       | 
       | (BTW, I do write test targets for every file in my C projects,
       | but I'm not so much into jogging).
       | 
       | Those things aren't that obvious, and I'd rather not have them
       | added to C.
       | 
       | Wrong language.
        
         | EuAndreh wrote:
         | I have my own list of things that could "easily" be added to C,
         | but I'd rather them not to be.
        
           | WalterBright wrote:
           | You get them anyway in the form of extensions.
        
             | EuAndreh wrote:
             | Thanks, but no thanks.
        
         | WalterBright wrote:
         | > forward declaration requirement enables a single-pass
         | compiler to emit code on-the-fly.
         | 
         | True, I know all about that. My Zortech C and C++ compiler was
         | one pass (after the multiple preprocessing passes). The ground
         | up ImportC C compiler completed a couple years ago has a
         | separate parse pass.
         | 
         | So I well know the tradeoffs. The parser being stand-alone
         | means it is much simpler to understand and unittest. I found no
         | advantage to a single pass compiler. It isn't any faster.
         | 
         | > simply adding things onto C gets you C++
         | 
         | C++ doesn't allow forward declarations either.
         | 
         | Successfully doing a parse-only on C code doesn't quite work.
         | It turns out the grammar relies on a symbol table. Fortunately,
         | only a symbol table of the typedefs. Once adding that in,
         | ImportC worked. (I really tried to make it work without the
         | typedef symbol table!)
         | 
         | C++ added a bunch more syntax that relies on the symbol table.
         | I would not even try fixing it to work as parse-only.
         | 
         | > in C you don't solve problems by adding features, but by
         | writing more code in C
         | 
         | The trouble with such sayings is like following a google map
         | that says cross this bridge, but wasn't updated with news that
         | the bridge is out.
         | 
         | > Those things aren't that obvious,
         | 
         | They are once you use another language that doesn't have those
         | restrictions.
         | 
         | > and I'd rather not have them added to C.
         | 
         | C adds new things all the time to the Standard, like normalized
         | Unicode identifiers, which are a complete waste of time. Every
         | C compiler also adds a boatload of extensions, some good, some
         | wacky, many ineptly documented, all incompatible with every
         | other C compiler extensions.
        
           | EuAndreh wrote:
           | > The parser being stand-alone means it is much simpler to
           | understand and unittest.
           | 
           | Stand-aloneness and single-passness are orthogonal.
           | 
           | > I found no advantage to a single pass compiler. It isn't
           | any faster.
           | 
           | A gigantic advantage: a single-pass-compilable language is
           | simpler. By definition.
           | 
           | Implementations may or may not be simpler or faster.
           | 
           | > C++ doesn't allow forward declarations either.
           | 
           | Well, that's not what I meant.
           | 
           | C++ is "C with just this thing" done way too many times.
           | 
           | > The trouble with such sayings is like following a google
           | map that says cross this bridge, but wasn't updated with news
           | that the bridge is out.
           | 
           | TBH, I didn't really get this. Is this about sticking to C as
           | is, but it is outdated as is?
           | 
           | C would be outdated if it didn't have, say, long long for
           | 64-bit numbers. Having "true" be a keyword instead of a macro
           | doesn't change how outdated it is or isn't, just like
           | compile-time evaluation also doesn't.
           | 
           | > They are once you use another language that doesn't have
           | those restrictions.
           | 
           | I have used many, and I still don't find them obvious.
           | 
           | > C adds new things all the time to the Standard, like
           | normalized Unicode identifiers, which are a complete waste of
           | time.
           | 
           | I agree that many/most are a waste of time, and shouldn't be
           | added to C. The fact of C adding things to the standard all
           | the time shouldn't justify adding even more things, but make
           | one question if those are needed at all, and how to
           | accomplish the goal without it.
           | 
           | > Every C compiler also adds a boatload of extensions, some
           | good, some wacky, many ineptly documented, all incompatible
           | with every other C compiler extensions.
           | 
           | I know about that, and my position is the same: just don't.
           | 
           | I don't use them also.
        
             | Dylan16807 wrote:
             | > A gigantic advantage: a single-pass-compilable language
             | is simpler. By definition.
             | 
             | That's only "by definition" if you take a language that
             | needs multiple passes, then remove the features that need
             | multiple passes, and don't replace them with anything else
             | to compensate.
             | 
             | The "by definition simpler" version of C would not only
             | disallow forward references, it would have no forward
             | declarations either. As-is, forward declarations add some
             | complexity of their own.
             | 
             | (Also, if you can figure out a way to emit jump
             | instructions in a single pass, you can probably figure out
             | a way to call unknown functions in a single pass.)
        
               | EuAndreh wrote:
               | I have the impression you're mixing single-pass
               | compilation and O(1) memory use of the compiler.
               | 
               | As is, C already is single-pass compilable, modulo some
               | unnecessary syntax ambiguities.
               | 
               | As the compiler reads the text, it marks some character
               | strings as tokens, these tokens are grouped as a fragment
               | of code, and some fragments of code are turned into
               | machine code. A simple function of a 100 lines doesn't
               | need to be parsed until the end for the compiler to start
               | emitting machine code.
               | 
               | Like the parser, this requires memory to keep tabs of
               | information and doesn't work for all types of constructs,
               | like a jump instruction to a label defined later in a
               | function. The code emitter soaks input untill it is
               | possible, and does so, like when the label is already
               | known and can be jumped to.
        
               | WalterBright wrote:
               | You cannot do any optimization when generating machine
               | code that way. That's fine for a primitive compiler built
               | for a school project, but not much else. (Even "no
               | optimization" switch settings on a compile do a lot of
               | optimizations, because otherwise the code quality is
               | execrable.)
        
               | EuAndreh wrote:
               | > That's fine for a primitive compiler built for a school
               | project, but not much else.
               | 
               | Not true.
               | 
               | On the one hand, just see how many non-compiled languages
               | are used outside of primitive school projects.
               | 
               | On the other hand, this simpler approach is actually
               | faster for writing actually fqst compilers. Many modern
               | compiled languages have compilers that work on the order
               | of ~100ms on a simple file with 1k LoC, when it could
               | (and arguably should) work on the order of ~1ms, IOW,
               | imperceptible given the syscalls overhead.
               | 
               | A 100x faster compiler that generates meh code is more
               | useful 99% of the time: when one is recompiling all the
               | time during development.
        
               | WalterBright wrote:
               | Doing jump instructions in a single pass is done by
               | creating a patch list, and when the compilation is done
               | walking the patch list and "fixing them up".
               | 
               | Doing this with functions is a lot more difficult,
               | because one cannot anticipate the argument types and
               | return types, which downstream influence the code
               | generation. Of course, early C would just assume such
               | forward references had integer arguments and integer
               | types, but that has long since fallen by the wayside.
        
       | pansa2 wrote:
       | > _the compiler only knows about what lexically precedes it.
       | [...] it drives programmers to lay out the declarations
       | backwards. The leaf functions come first, and the global
       | interface functions are last. It's like reading a newspaper
       | article from the bottom up. It makes no sense._
       | 
       | Defining functions on a "bottom-up" order like this is common
       | even in languages like Python which allow forward references. [0]
       | 
       | Is that just a holdover from languages which don't allow such
       | references? Or does it actually make more sense for certain types
       | of code?
       | 
       | [0] https://stackoverflow.com/a/73131538
        
         | almostgotcaught wrote:
         | You're confused. There are no forward references in python.
         | It's simply that identifiers in the body of a function aren't
         | resolved until the function itself is executed (if ever) and at
         | the point everything in the module scope has been defined. You
         | can test this yourself by just putting any name into a body and
         | loading the module.
        
           | pansa2 wrote:
           | > _identifiers in the body of a function aren 't resolved
           | until the function itself is executed (if ever) and at the
           | point everything in the module scope has been defined_
           | 
           | Yes, so within a function you can refer to things that are
           | defined later in the module. Isn't that's a "forward
           | reference", even if the details are slightly different from
           | how they work in D?
        
         | throwuxiytayq wrote:
         | In my code, the public interface always tends to bubble
         | upwards, and implementation details go at the end of the file.
         | I don't even have a strict rule for this, it just reads more
         | cleanly and seems to make sense. Especially if the
         | implementation is large - you don't want to scroll through all
         | that before you get to the basics of what it all does. I'm
         | curious how everyone else does things.
        
       | dang wrote:
       | [stub for offtopicness]
        
         | ranger_danger wrote:
         | login-walled
        
           | acheong08 wrote:
           | Looks like a new feature. Articles don't render render on
           | xcancel
        
           | smitty1e wrote:
           | Have a go at this => https://t.co/KXRE5XvuoP
           | 
           | I used the https://publish.twitter.com thing against the
           | Xeet, then lifted the first HREF out of the embed goo.
        
             | nom wrote:
             | Doesn't work, the t.co link gives me:
             | 
             | > This page is not supported. > Please visit the author's
             | profile on the latest version of X to view this content.
        
               | smitty1e wrote:
               | Worth a try.
        
           | dwattttt wrote:
           | Curious. I don't have an account, and I can read it.
        
           | ciupicri wrote:
           | Try https://www.digitalmars.com/articles/Cobvious.html
        
         | meibo wrote:
         | Sorry to be off-topic, but I'm disappointed in this being on X,
         | it even comes with an ad built-in. We should be doing better.
         | Spinning up a single-site blog is easier than ever for
         | technically-minded folks - the internet needs to become
         | independent again.
        
           | WalterBright wrote:
           | I posted it originally on digitalmars.com, and nobody noticed
           | it. I posted it today on X, and bang! I didn't even submit it
           | to HackerNews.
           | 
           | I'll be using X more for articles in the future, but will
           | also put them on the dlang.org and digitalmars.com sites.
        
           | msm_ wrote:
           | It's also hosted here:
           | https://www.digitalmars.com/articles/Cobvious.html
        
         | WalterBright wrote:
         | I didn't know X put articles behind a paywall? I haven't tried
         | putting articles there before.
         | 
         | Anyhow, here's the same article:
         | 
         | https://www.digitalmars.com/articles/Cobvious.html
         | 
         | Fun fact: X's article formatter recognizes D code!
        
           | wging wrote:
           | I'm not sure what you are seeing, but perhaps it's just a
           | login wall. I was able to read it; I'm logged in but have
           | never paid for Twitter or X. X does tend to hide certain
           | things (such as replies and replied-to tweets) if you're not
           | logged in.
        
             | kurisufag wrote:
             | it's not visible to nitter, which is annoying.
        
             | WalterBright wrote:
             | I'm already logged in, which I infer is why I didn't see a
             | login wall. Anyhow, I also posted an alternate link.
        
           | sixthDot wrote:
           | Haven't you noticed that the homepage does not include the X
           | widget anymore ? It's been removed [1] exactly because of
           | that, i.e the content is not public anymore.
           | 
           | [1]: https://github.com/dlang/dlang.org/pull/3714
        
           | dang wrote:
           | We'd always rather go with the more specific site anyhow so
           | I've changed the URL to that from
           | https://twitter.com/WalterBright/status/1878209651306803406.
           | Thanks!
        
         | honestSysAdmin wrote:
         | Perhaps I could load the Javascript and also "login to X", but
         | I'll instead forego reading this and pay attention to what
         | others are writing about C.
        
           | msm_ wrote:
           | It's also hosted here:
           | https://www.digitalmars.com/articles/Cobvious.html
        
       | TheNewAndy wrote:
       | Header files are one of the things I miss the most about
       | languages that aren't C. Having a very clear distinction between
       | public and private, and interface and implementation is one of my
       | favourite things about C code (at least the way I write it).
       | 
       | Being able to just read through a library's .h files to know how
       | to use it is really nice. Typically, my .h files don't really
       | look like my .c files because all the documentation for how to
       | use the thing lives in the .h file (and isn't duplicated in the
       | .c file). It would be entirely possible to put this documentation
       | into the .c file, but it makes reading the interface much less
       | pleasant for someone using it.
        
         | legobmw99 wrote:
         | Some other languages have equivalents (OCaml comes to mind),
         | but usually they're less necessary
        
         | Lvl999Noob wrote:
         | I don't really program in C much so please correct me if I am
         | wrong. There is a flaw in header files in that they work the
         | exact same for dynamic vs static linking, right? If I am making
         | a library in C for static linking, I need to put my internal
         | details in the header file if I want the user's compiler to be
         | able to use those details. But putting them in the header files
         | also means they are part of the public interface now and should
         | no longer be changed.
         | 
         | Basically, I cannot do something like a struct with an opaque
         | internal structure but a compile time known layout so that the
         | compiler can optimise it properly but the user cannot mess with
         | the internals (in language supported direct ways).
        
           | TheNewAndy wrote:
           | That is less about header files, and more about how machine
           | code works.
           | 
           | If you want to have some abstract type where you don't let
           | people know anything about the innards, but you do have an
           | explicit interface which enumerates what you can do with it,
           | then yes - you can only really pass around pointers to these
           | things and people outside your abstraction can only pass
           | references not values.
           | 
           | If you want people to be able to pass your abstract type by
           | value (among other things), then either you need to let them
           | know how big the thing is (an implementation detail) or you
           | have to expose the copy implementation in such a way that it
           | could be inlined (more implementation details).
           | 
           | Sometimes, the "pure abstraction" approach is best where you
           | only ever deal with pointers to things, and other times the
           | "let's pretend that people do the right thing" approach is
           | best. I don't see this as a header file thing though.
        
             | Lvl999Noob wrote:
             | I disagree with you on this. In another language with
             | explicit public / private separation, the compiler can have
             | access to the internal layout of a type (and thus optimise
             | on it) without letting the developer mess around with it
             | directly. I am assuming static compilation of course.
             | Across a dynamic boundary, I would expect this compiler to
             | behave like a normal C compiler and not use that layout.
             | 
             | In a header file, the information for the compiler and the
             | user are the exact same which means you can't reduce your
             | public interface without straight up hiding more of
             | yourself.
        
               | TheNewAndy wrote:
               | Personally, I'm happy to just let a Sufficiently Advanced
               | Compiler do link time optimizations to deal with that
               | level of optimization and either take the hit, or make
               | more things public while that compiler doesn't exist.
               | 
               | Let the header files be written for people to read first,
               | and only if there is actually a big performance issue,
               | and the problem is the interface do you need to revisit
               | it (and I'm not just saying this - I will frequently and
               | happily go back and modify interfaces to allow for less
               | data movement etc, but most of the time it really isn't
               | important).
               | 
               | I think you are probably right to disagree with me though
               | - I think I should have said that it is more of a
               | limitation on how object files work, rather than how
               | machines work. Object files aren't the only way things
               | can work.
        
               | uecker wrote:
               | A compiler can still have access to the internal layout
               | in C via link-time optimization.
        
               | johannes1234321 wrote:
               | Linker is something different from Compiler (even if
               | often called via same Frontend)
        
               | uecker wrote:
               | The linker invokes the compiler again.
        
             | saidinesh5 wrote:
             | I think that's a problem with C's header files.
             | 
             | With C++ you have the third option where the compiler makes
             | sure that the "people will do the right thing" with the
             | private keyword - assuming they're not doing some weird
             | pointer math to access the private members..
             | 
             | Of course, you'll have to deal with ABI stability now but
             | it's all tradeoffs for what your requirements are.
        
               | TheNewAndy wrote:
               | Right, but as soon as you have private stuff in your
               | header file, that is leaking implementation details. Yes
               | it is kind of true that these are compile time checked to
               | make sure that people don't do the wrong thing, but it is
               | still an implementation detail that is leaking.
               | 
               | It comes down to a cost benefit thing - is the cost of
               | poorer readability worth it for mitigating the risk of
               | people doing the wrong thing? My experience says no,
               | other people's experience says yes. Probably we are
               | working on different problems and with different teams.
        
               | Mankaninen wrote:
               | Of course you should do the right thing, but if you want
               | to break the private of C++ it is much easier to "#define
               | private public" before including the header file.
        
               | saidinesh5 wrote:
               | now that's just diabolical...
        
           | lzsiga wrote:
           | The proper way is not exporting implementation details at
           | all, instead define opaque types in your header files like
           | this: `typedef struct ssl_st SSL;`. This comes from OpenSSL,
           | it means users can use `SSL *` pointers, but they don't know
           | what those pointers point to.
           | 
           | Of course you can also have internal header-files within your
           | own project, which you don't share with the end-users of your
           | product.
        
           | uecker wrote:
           | In C programs only the external definition of an interface
           | goes into the header of a library but not implementation
           | details (there could be headers intentionally exposing
           | details for internal use, of course).
           | 
           | The problem is real for C++.
           | 
           | Optimizers can look across translation units nowadays (link-
           | time optimization), so there is no reason to expose internal
           | details in a header for this. For dynamic libraries this does
           | not work of course, but it also shouldn't.
        
             | johannes1234321 wrote:
             | Even for C it's normal ways true: When size of structures
             | have to be known to the user (if they are supposed to keep
             | them on stack or mallox themselves or whatever) the
             | "private" structure often ends up in a "private" header.
             | (There are ways to still hide it, but they cause work to
             | keep things proper)
             | 
             | And then there are cases where (often die to performance)
             | you want inlining of some operations without relying on
             | Link time optimisation, then implementation has to go to
             | headers, too.
        
         | kouteiheika wrote:
         | > Header files are one of the things I miss the most about
         | languages that aren't C. Having a very clear distinction
         | between public and private, and interface and implementation is
         | one of my favourite things about C code (at least the way I
         | write it).
         | 
         | I always found this argument baffling, because the way some
         | other language solve this problem is with tooling, which is a
         | much better way to do it in my opinion.
         | 
         | Take Rust for example. You want to see the interface of a given
         | library and see how to use it? Easy. Type in `cargo doc --open`
         | and you're done. You get a nice interface with fully searchable
         | API interface with the whole public API, and it's all
         | automatic, and you don't have to manually maintain it nor have
         | to duplicate code between your header and your source file.
        
           | panic wrote:
           | As someone who likes C header files, I enjoy manually
           | maintaining them. Designing the interface separately from the
           | implementation feels good to me, and a well-structured .h
           | file is nicer to read than any auto-generated docs I've
           | encountered.
        
             | chii wrote:
             | > Designing the interface separately from the
             | implementation feels good to me
             | 
             | would you make the same argument for java then?
        
               | otteromkram wrote:
               | Absolutely not.
        
               | tpoacher wrote:
               | you don't like java interfaces?
        
               | marginalia_nu wrote:
               | Even as a java programmer, I think this is a bad take.
               | Java doesn't force separation of implementation and
               | interface, and java interfaces also have a lot of weird
               | stuff going on with them.
               | 
               | Java also has too many tools for this. You both have
               | class/interface, but also public/private. I honestly
               | think C does it better than Java.
        
               | layer8 wrote:
               | It's important to not conflate Java's _interface_ keyword
               | with the more general notion of "interface" as in "API".
               | In Java, you generally don't and can't have a source-
               | level representation of the API without it being
               | interspersed with its implementation.
               | 
               | (You could imagine such a representation, i.e. remove all
               | method bodies and (package-)private elements, but the
               | result wouldn't be valid Java. IDEs arguably could and
               | should provide such a source-level view, e.g. via code
               | folding, but I don't know any that do.)
        
             | xigoi wrote:
             | The problem is that you have to write the interface not
             | only separately grom the implementation, but together with
             | the implementation as well, which leads to duplication of
             | information;
        
               | bluGill wrote:
               | So? Yes you need some duplicatin but it forces you to put
               | information useful to api users where they won't have to
               | wade through pages of information not of interest to
               | them. eventually they may need to read the source but a
               | lot of common questions are answered by the header and so
               | the small cost to make them is worth it.
               | 
               | of course javadoc can answer the same questions but then
               | you have to run it.
        
           | TheNewAndy wrote:
           | This is probably something where it comes down to preference
           | and familiarity. I would much prefer a simple text file for
           | documentation that I can grep, open in my text editor, modify
           | easily without switching context (oh, I should have been more
           | explicit in the documentation I wrote - let me just fix that
           | now), etc. All the features you mentioned "nice interface,
           | fully searchable API interface, whole public API" are exactly
           | what you get if you open a well written header file in any
           | old text editor.
           | 
           | I used to be a big fan of doxygen etc, but for the stuff I've
           | worked on, I've found that "pretty" documentation is way less
           | important than "useful" documentation, and that the
           | reformatting done by these tools tends to lead towards worse
           | documentation with the people I have worked with ("Oh, I need
           | to make sure every function argument has documentation, so I
           | will just reword the name of the argument"). Since moving
           | away from doxygen I have stopped seeing this behaviour from
           | people - I haven't tried to get a really good explanation as
           | to why, but the quality of documentation has definitely
           | improved, and my (unproven) theory is that keeping the
           | presentation as plain as possible means that the focus turns
           | to the content.
           | 
           | I don't know if rust doc suffers the same issues, but the
           | tooling you are mentioning just seems to add an extra step
           | (depending on how you count steps I suppose, you could
           | perhaps say it is the same number of steps...) and provide no
           | obvious benefit to me (and it does provide the obvious
           | downside that it is harder to edit documentation when you are
           | reading it in the form you are suggesting).
           | 
           | But with all these things, different projects and teams and
           | problem domains will probably tend towards having things that
           | work better or worse.
        
             | metadat wrote:
             | > _well written text file_
             | 
             | The problem with this is no one agrees on the definition of
             | "well-written", so consistency is a constant battle and
             | struggle. Language tooling is a better answer for quality
             | of life.
        
               | TheNewAndy wrote:
               | That's an interesting assertion, but not one that matches
               | the experience I've had.
               | 
               | It is one of those things that sounds "obviously true",
               | but in practice I've found that it doesn't really live up
               | to the promise. As a concrete example of this, having a
               | plain text header file as documentation tends to mean
               | that when people are reading it, if they spot a mistake
               | or see that something isn't documented that should be
               | documented, they are much more likely to fix it than if
               | the documentation is displayed in a "prettier" form like
               | HTML.
               | 
               | The problem with header files that aren't "well-written"
               | tends to be that the actual content you are looking for
               | isn't in there, and no amount of language tooling can
               | actually fix that (and can be an impediment towards
               | fixing it).
        
               | tpoacher wrote:
               | I'll second this in Java land. I much prefer reading the
               | sources directly than javadocs. Though jshell also comes
               | in handy.
        
               | josephg wrote:
               | I have the same experience a lot of the time with 3rd
               | party rust crates. Doc.rs is amazing - but it's rare that
               | I'll use a library without, at some point, hitting view
               | source.
        
               | sim7c00 wrote:
               | for most rust ive done (not tons) the docs were very
               | basic as onky auto generated with minimal content.
               | totally useless, have to read sources to find out what is
               | in there. auto documentation to me ia just ti satisfy
               | people who need to tick all of these boxes and want to do
               | with minimal effort. has dox has tests etc. such artitude
               | never leads to quality.
        
               | bluGill wrote:
               | Useful documentation is impossible for the developer to
               | write - they are too close to the code and so don't
               | understand what the users (either api users or end users)
               | need to know. Developers agonize over details that users
               | don't care about while ignoring as obvious important
               | things users don't know.
        
               | josephg wrote:
               | I know people look at me like I'm a heathen and a
               | scoundrel, but I think a lot of software teams spend too
               | much time trying to make things consistent. Where's the
               | ROI? There is none.
               | 
               | GitHub readmes? Bring on the weird quirks, art, rants
               | about other software, and so on. I'll take it all.
               | 
               | Don't get me started on linters. Yes, there's lots of
               | things that should actually be consistent in a codebase
               | (like indentation). But for every useful check, linters
               | have 100 random pointless things they complain about. Oh,
               | you used a ternary statement? Boo hoo! Oh, my JavaScript
               | has a mix of semicolons and non semicolons? Who cares?
               | The birds are singing. Don't bother me with this shite.
               | 
               | Software is a creative discipline. Bland software
               | reflects a bland mind.
        
               | XorNot wrote:
               | I don't like complaining linters. I do like auto fixing
               | linters I can leave running in the background.
        
               | lazystar wrote:
               | > auto fixing linters
               | 
               | any advice on how to implement an auto linter in an old
               | codebase? i hate losing the git blame info.
        
               | lazystar wrote:
               | > Where's the ROI? There is none.
               | 
               | > Oh, my JavaScript has a mix of semicolons and non
               | semicolons? Who cares?
               | 
               | i had to refactor and port a javascript codebase that
               | contained a mix of all of javascripts syntactic sugar, no
               | comments anywhere in the codebase, and i was unable to
               | ask the original devs any questions. the high amount of
               | syntactic sugar gave me "javascript diabetes" - it was
               | fun figuring out all the randomness, but it delayed the
               | project and has made it extremely difficult to onboard
               | new folks to the team after i completed the port.
               | 
               | painting is a creative discipline, and the mona lisa has
               | stood the test of time because davinci used a painting
               | style and materials that set the painting up for long
               | term use.
               | 
               | a codebase without standards is akin to drawing the mona
               | lisa on a sidewalk with sidewalk chalk.
        
             | kouteiheika wrote:
             | > All the features you mentioned "nice interface, fully
             | searchable API interface, whole public API" are exactly
             | what you get if you open a well written header file in any
             | old text editor.
             | 
             | No, you can't, and it's not even close.
             | 
             | You have a header file that's 2000 lines of code, and you
             | have a function which uses type X. You want to see the
             | definition of type X. How do you quickly jump to its
             | definition with your "any old text editor"? You try to grep
             | for it in the header? What if that identifier is used 30
             | times in that file? Now you have to go through all of other
             | 29 uses and hunt for the definition. What if it's from
             | another header file? What if the type X is from another
             | library altogether? Now you need to manually grep through a
             | bunch of other header files and potentially other
             | libraries, and due to C's include system you often can't
             | even be sure where you need to grep on the filesystem.
             | 
             | Anyway, take a look at the docs for one of the most popular
             | Rust crates:
             | 
             | https://docs.rs/regex/1.11.1/regex/struct.Regex.html
             | 
             | The experience going through these docs (once you get used
             | to it) is night and day compared to just reading header
             | files. Everything is cross linked so you can easily cross-
             | reference types. You can easily hide the docs if you just
             | want to see the prototypes (click on the "Summary" button).
             | You can easily see the implementation of a given function
             | (click on "source" next to the prototype). You can search
             | through the whole public API. If you click on a type from
             | another library it will automatically show you docs for
             | that library. You have usage examples (*which are
             | automatically unit tested so they're guaranteed to be
             | correct*!). You can find non-obvious relationships between
             | types that you wouldn't get just by reading the source code
             | where the thing is defined (e.g. all implementations of a
             | given trait are listed, which are usually scattered across
             | the codebase).
             | 
             | > I don't know if rust doc suffers the same issues, but the
             | tooling you are mentioning just seems to add an extra step
             | (depending on how you count steps I suppose, you could
             | perhaps say it is the same number of steps...) and provide
             | no obvious benefit to me (and it does provide the obvious
             | downside that it is harder to edit documentation when you
             | are reading it in the form you are suggesting).
             | 
             | Why would I want to edit the documentation of an external
             | library I'm consuming when I'm reading it? And even if I do
             | then the effort to make a PR changing those docs pales in
             | comparison to the effort it takes to open the original
             | source code with the docs and edit it.
             | 
             | Or did you mean editing the docs for _my_ code? In that
             | case I can also easily do it, because docs are part of my
             | source files and are maintained alongside the
             | implementation. If I change the implementation I have docs
             | right there _in the same file_ and I can easily edit them.
             | Having to open the header file and hunt for the declaration
             | to edit the docs  "just seems to add an extra step" and
             | "and provide no obvious benefit to me", if I may use your
             | words. (:
        
               | mtlmtlmtlmtl wrote:
               | Most decent text editors support something like go to
               | definition. Your entire comment seems to be based on the
               | idea that text editors only support basic search, which
               | is simply false.
               | 
               | Personally I'm quite content with both experiences. But
               | it really is just a matter of preference.
        
               | uecker wrote:
               | At least moderately advanced text editors often
               | interoperate with symbols tables, so you can jump to a
               | definition. But even with grep, you can usually do it in
               | a way where you differentiate between definition and use.
               | But I am not arguing that you should not use advanced
               | tools if you like is, the deeper point is that you can
               | always use advanced tools even with headers, but you can
               | not go back in a language designed around advanced tools
               | and work with simple tools. So it is strictly inferior
               | IMHO to design a language around this additional
               | cmplexity.
        
               | TheNewAndy wrote:
               | Thanks for the constructive example of the rust doc.
               | 
               | I am not making things up when I say that the very first
               | question I had about how to use this module, either is
               | not answered, or I couldn't find the answer. That
               | question was "what regular expression syntax is
               | supported?". This is such a fundamental question, yet
               | there is no answer provided.
               | 
               | As a preference thing, I don't really like examples in
               | APIs (it is supposed to be a reference in my opinion) and
               | I find them to be mostly noise.
               | 
               | > Why would I want to edit the documentation of an
               | external library I'm consuming when I'm reading it? And
               | even if I do then the effort to make a PR changing those
               | docs pales in comparison to the effort it takes to open
               | the original source code with the docs and edit it.
               | 
               | Right, this is possibly where our experiences differ. I'm
               | frequently pulling in loads of code, some of which I've
               | written, some of which other people have written, and
               | when I pull in code to a project I take ownership of it.
               | Doesn't matter who wrote it - if it is in my project,
               | then I'm going to make sure it is up to the standards I
               | expect. A lot of the time, the code is stuff I've written
               | anyway, which means that when I come back in a few months
               | time and go to use it, I find that things that seemed
               | obvious at the time might not be so obvious, and a simple
               | comment can completely fix it. Sometimes it is a comment
               | and a code change ("wouldn't it be nice if this function
               | handled edge case X nicely? I'll just go in there and fix
               | it").
               | 
               | The distinction between external and internal that you
               | have looks pretty different to me, and that could just be
               | why we have different opinions.
        
               | reanimus wrote:
               | > I am not making things up when I say that the very
               | first question I had about how to use this module, either
               | is not answered, or I couldn't find the answer. That
               | question was "what regular expression syntax is
               | supported?". This is such a fundamental question, yet
               | there is no answer provided.
               | 
               | The main page for the documentation answers that
               | question: https://docs.rs/regex/1.11.1/regex/index.html
               | 
               | It even says "If you just want API documentation, then
               | skip to the Regex type", which is what you were linked to
               | before.
        
               | davisoneee wrote:
               | The parent linked to a subsection showing usage for a
               | particular object. If you click back into the root level
               | for the document there is a header specifying 'syntax',
               | and other more 'package-level' documentation
        
               | kouteiheika wrote:
               | > I am not making things up when I say that the very
               | first question I had about how to use this module, either
               | is not answered, or I couldn't find the answer. That
               | question was "what regular expression syntax is
               | supported?". This is such a fundamental question, yet
               | there is no answer provided.
               | 
               | This is a fair question to have. As others have already
               | said, this is the API reference for a particular class,
               | so you won't get the high level details here. You can
               | click in the upper left corner to go to the high level
               | docs for the whole library.
               | 
               | > The distinction between external and internal that you
               | have looks pretty different to me, and that could just be
               | why we have different opinions.
               | 
               | Well, there are two "external" vs "internal" distinctions
               | I make:
               | 
               | 1. Code I maintain, vs code that I pull in as an external
               | dependency from somewhere else (to give an example,
               | something like libpng, zlib, etc.). So if I want to fix
               | something in the external dependency I make a pull
               | request to the original project. Here I need to clone the
               | original project, find the appropriate files to edit,
               | edit them, make sure it compiles, make sure the tests
               | pass, make a PR, etc. Having the header file immediately
               | editable doesn't net me anything here because I'm not
               | going to edit the original header files to make the
               | change (which are either installed globally on my system,
               | or maintained by my package manager somewhere deep under
               | my /home/).
               | 
               | 2. Code that is part of my current project, vs code that
               | is a library that I reuse from another of my projects.
               | These are both "internal" in a sense that I maintain
               | them, but to my current project those are "external"
               | libraries (I maintain them separately and reuse in
               | multiple projects, but I don't copy-paste them and
               | instead maintain only one copy). In this case it's a fair
               | point that if you're browsing the API reference it's
               | extra work to have to open up the original sources and
               | make the change there, but I disagree that it's making
               | things any harder. I still have to properly run any
               | relevant unit tests of the library I'm modifying, still
               | have to make a proper commit, etc., and going from the
               | API reference to the source code takes at most a few
               | seconds (since the API reference will tell me which exact
               | file it is, so I just have to tell my IDE's fuzzy file
               | opener to open up that file to me.) and is still a tiny
               | fraction of all of the things I'd need to do to make the
               | change.
        
               | hawski wrote:
               | Depending on coding style you could just do something
               | like this:                 ^struct whatever
        
               | SkiFire13 wrote:
               | The issue is that the coding style depends on whoever
               | wrote the external library, not on you, so this ends up
               | working only sometimes. You can probably find some other
               | combination that will help you find what you're looking
               | for (I do this all the time when using Github's web
               | interface) but ultimately this is just a bad experience.
        
               | brabel wrote:
               | I think the person you're responding to must know all of
               | this. This is stuff that's obvious to anyone who has ever
               | written any code that required using libraries.
               | Unfortunately , people like to pretend to have a gripe
               | with something on the internet just for the sake of
               | arguing. This is the only conclusion I can arrive at when
               | people appear to seriously propose reading a header file
               | in a text editor is somehow better than reading
               | documentation in a purposefully designed documentation
               | format. It's like saying browsers are just a waste of
               | time when you can just use Gopher for everything.
        
               | hyperold wrote:
               | Or it just might be that different people prefer
               | different things. I'm a hardcore fan of header files too.
               | Vim is my preferred way of dealing with text and I can do
               | all kinds of magic with it with the speed of though and I
               | prefer to use as plain as possible text files. In the
               | rare occasion when the documentation needs more than
               | ascii stuff it's best practice to write a nice tex and
               | friends documentation plus a real tutorial anyway. And
               | full literate programming style is hard to beat when you
               | are dealing with complex things.
        
               | almostdeadguy wrote:
               | It's fine to have preferences or cognitive inertia
               | towards working a certain way. It's silly to pretend that
               | doing things this way conveys some kind of universalist
               | advantage or to conjure up a bunch of imaginary/highly
               | niche scenarios (I'm remote coding over 28.8k at the
               | bottom of the ocean and have no access to a browser
               | anywhere!) that necessitate working this way for
               | argumentative purposes.
        
               | marcosdumay wrote:
               | The OP uses C libraries, and this is used to much simpler
               | interfaces and much smaller dependency sets than the GP.
               | So no, I don't think they know all of this.
               | 
               | But also, they probably to know how to keep their
               | dependencies sane, and possibly think the best way to
               | document that giant 2k lines interface is in a book. What
               | are both really good opinions, that will never be really
               | "understood" by communities the GP takes his libraries
               | from just because it's not viable for them to do it.
        
             | thayne wrote:
             | > and that the reformatting done by these tools tends to
             | lead towards worse documentation with the people I have
             | worked with ("Oh, I need to make sure every function
             | argument has documentation, so I will just reword the name
             | of the argument")
             | 
             | That seems like an orthogonal issue to me. I've seen places
             | where documentation is only in the source code, no
             | generated web pages, but there is a policy or even just
             | soft expectation to document every parameter, even if it
             | doesn't dd anything. And I've also seen places that make
             | heavy use of these tools that doesn't have any such
             | expectation.
        
             | Yoric wrote:
             | Have you looked at how OCaml does it?
             | 
             | The historical way is to have a .ml and a .mli files. The
             | .ml file contains the implementation. Any documentation in
             | that file is considered implementation detail, will not be
             | published by ocamldoc. The .mli file contains everything
             | users need to know, including documentation, function
             | signatures, etc.
             | 
             | Interestingly, the .mli and the .ml signatures do not
             | necessarily need to agree. For instance, a global variable
             | in the .ml does not need to be published in the .mli. More
             | interestingly, a generic function in the .ml does not need
             | to be exposed as generic in the .mli, or can have more
             | restrictions.
             | 
             | You could easily emulate this in Rust, but it's not the
             | standard.
        
           | salgernon wrote:
           | I'm with parent - what if you don't have the tool? What if
           | there's a syntax error in some implementation or dependency
           | such that the tool chokes early?
           | 
           | Human readable headers are accessible out of context if the
           | implementation. They also help provide a clear abstraction -
           | this is the contract. This is what I support as of this
           | version. (And hopefully with appropriate annotations across
           | versions)
        
             | kouteiheika wrote:
             | > I'm with parent - what if you don't have the tool?
             | 
             | The "what if you don't have the tool" situation never
             | happens in case of Rust. If you have the compiler you have
             | the tool, because it's always included with the compiler.
             | This isn't some third party tool that you install manually;
             | it's arguably part of the language.
             | 
             | > What if there's a syntax error in some implementation or
             | dependency such that the tool chokes early?
             | 
             | In C I can see how this can happen with its mess of a build
             | systems; in Rust this doesn't happen (in my 10+ years of
             | Rust I've never seen it), because people don't publish
             | libraries with syntax errors (duh!).
        
               | TheNewAndy wrote:
               | "Never" is a big call.
               | 
               | In this specific case, your tool requires a web browser
               | (though I'm assuming that there is a non-web browser form
               | of what is being sold here). Maybe you are in a situation
               | where you only have terminal access to the machine.
               | 
               | Maybe you are on your phone just browsing github looking
               | for a library to use
               | 
               | I'm sure people can continue to imagine more examples. It
               | is entirely possible that we have different experiences
               | of projects and teams.
        
               | nindalf wrote:
               | > I'm sure people can continue to imagine more examples
               | 
               | Hopefully they'll imagine more compelling examples.
               | 
               | If the hypothetical person's phone is capable of browsing
               | GitHub, I don't see why they can't also browse docs.rs.
               | It renders well on small screens. That's not a
               | hypothetical, I've actually read the docs for libraries
               | on my phone.
        
               | jmb99 wrote:
               | > The "what if you don't have the tool" situation never
               | happens in case of Rust.
               | 
               | So it's built into GitLab and GitHub? BitBucket? How easy
               | is it to use on windows (i.e. is it is easy as opening a
               | .h in notepad and reading it)? How easy is it to use from
               | a command line environment with vim or emacs bindings?
               | 
               | I could go on. "Never" is doing a _lot_ of heavy lifting
               | in your assertion. I shouldn't have to install a
               | toolchain (let alone rely on a web browser) to read API
               | documentation.
        
               | Dylan16807 wrote:
               | > I shouldn't have to install a toolchain (let alone rely
               | on a web browser) to read API documentation.
               | 
               | Why are you reading a library API for a language you're
               | not coding in?
               | 
               | I'm sure you can come up with some situation, but that
               | situation should NOT be what we optimize for.
               | 
               | And web browsers are fine.
               | 
               | > is it is easy as opening a .h in notepad and reading it
               | 
               | If you include the actual ease of reading, yeah it should
               | be.
        
               | nindalf wrote:
               | > I could go on
               | 
               | Please do. It just sounds like you're nitpicking.
               | 
               | If you can open a browser, open docs.rs. The GitHub repo
               | usually contains a link to docs.rs because that's how
               | people prefer to read the documentation.
               | 
               | If you prefer working without the internet that's fine
               | too. Use cargo doc, which opens the rendered doc page in
               | a local web browser.
               | 
               | If you prefer being in a text editor exclusively, no
               | problem! Grep for `pub` and read the doc comments right
               | above (these start with ///). No toolchain necessary.
               | 
               | Look, most normal people don't have some intense phobia
               | of web browsers, so they'd prefer docs.rs. For the people
               | who prefer text editor, it's still a great experience -
               | git clone and look for the doc comments.
               | 
               | The point is, the existence of docs.rs only encourages
               | Rust library developers to write more and better
               | documentation, which everyone, including text editor
               | exclusive people benefit from. That's why your comment
               | sounds so strange.
        
               | kouteiheika wrote:
               | > So it's built into GitLab and GitHub? BitBucket?
               | 
               | No. It's built into the toolchain which every Rust
               | developer has installed.
               | 
               | > How easy is it to use on windows (i.e. is it is easy as
               | opening a .h in notepad and reading it)?
               | 
               | A easy as on Linux or macOS from my experience.
               | 
               | > How easy is it to use from a command line environment
               | with vim or emacs bindings?
               | 
               | Not sure I understand the question; use how exactly? You
               | either have a binding which runs `cargo doc` and opens
               | the docs for you, or you use an LSP server and a plugin
               | for your editor in which case the docs are integrated
               | into your editor.
               | 
               | > I shouldn't have to install a toolchain (let alone rely
               | on a web browser) to read API documentation.
               | 
               | If you want you can just read the source code, just as
               | you do for any other language, because the docs are right
               | there in the sources.
               | 
               | For publicly available libraries you can also type in
               | `https://docs.rs/$name_of_library` in your web browser to
               | open the docs. Any library available through crates.io
               | (so 99.9% of what people use) have docs available there,
               | so even if you don't have the toolchain installed/are on
               | your phone you can still browse through the docs.
               | 
               | I know what you're going to say - what if you don't have
               | the toolchain installed and the library is not public?
               | Or, worse, you're using a 30 year old machine that
               | doesn't have a web browser available?! Well, sure, tough
               | luck, then you need to do it the old school way and
               | browse the sources.
               | 
               | You can always find a corner case of "what if...?", but I
               | find that totally unconvincing. Making the 99.9% case
               | harder (when you have a web browser and a toolchain
               | installed, etc.) to make the 0.1% case (when you don't)
               | easier is a bad tradeoff.
        
               | Brian_K_White wrote:
               | I don't understand how you don't understand the order of
               | magnitude difference in flexibility, utility,
               | availability, etc between needing to run a specific
               | executable vs merely opening a text file in any way.
               | 
               | "you always have the exe" is just not even remotely a
               | valid argument.
        
               | nindalf wrote:
               | I don't understand how you don't understand that that's
               | always an option. Rust source files are written in
               | plaintext too.
               | 
               | There are a few people in this thread, including you, who
               | claim that they vastly prefer the output of documentation
               | to be plain text in a single file rather than linked HTML
               | files OR reading the source in multiple plaintext files.
               | 
               | That's a preference, so y'all can't be wrong. But
               | consider that if this preference was even slightly
               | popular, cargo doc would probably get a ---text option
               | that output everything in a single text file. The fact
               | that it doesn't have it tells me that this preference is
               | very niche.
        
               | kouteiheika wrote:
               | > "you always have the exe" is just not even remotely a
               | valid argument.
               | 
               | Why? Can you explain it to me?
               | 
               | I'm a Rust developer. I use my work station every day for
               | 8 hours to write code. I also use `cargo doc` (the tool
               | for which "I always have the exe") every day to look up
               | API docs, and in total this saves me a ton of time every
               | month (probably multiple hours at least, if I'm working
               | with unfamiliar libraries), and I save even more time
               | because I don't have to maintain separate header files
               | (because Rust doesn't have them).
               | 
               | Can you explain the superior flexibility and utility of
               | "merely opening a text file" over this approach, and how
               | that would make me (and my colleagues at work) more
               | productive and save me time?
               | 
               | I'm not being sarcastic here; genuinely, please convince
               | me that I'm wrong. I've been a C developer for over 20
               | years and I did it the "opening a text file" way and
               | never want to go back, but maybe you're seeing something
               | here that I never saw myself, in which case please
               | enlighten me.
        
               | Dylan16807 wrote:
               | It's less available in rare situations.
               | 
               | It's not less flexible once you already took availability
               | into account.
               | 
               | It has _more_ utility, that 's the entire point.
        
               | pornel wrote:
               | Yes, it works with GitHub, GitLab, Bitbucket, and
               | everything else. It's built into the compiler toolchain.
               | 
               | It works with every syntax that you can compile, because
               | it uses the compiler itself to extract the documentation.
               | 
               | Yes, it works on Windows too. Rust supports Windows as a
               | first-class platform. It works with dependencies too (the
               | docs even link across packages). The fragmentation of C
               | tooling and unreliability/complexity of integrating with
               | C builds is not a universal problem.
               | 
               | Rust's built-in documentation generator creates HTML, so
               | anything with a browser can show it. It also has JSON
               | format for 3rd party tooling.
               | 
               | The same language syntax for the documentation is
               | understood by Rust's LSP server, so vim, emacs, and other
               | editors with LSP plugins can show the documentation
               | inline too.
               | 
               | I've been using this for years, and it works great. I
               | don't miss maintaining C headers at all. I write function
               | definitions once, document them in the same place where
               | the code is, and get high fidelity always up-to-date API
               | docs automatically.
        
             | gary_0 wrote:
             | The "what if you don't have the software" argument doesn't
             | hold water for me. What if you don't have git? What if you
             | don't have a text editor? What if you don't have a
             | filesystem?
             | 
             | Most programming language communities are okay with
             | expecting a certain amount of (modern) tooling, and C can't
             | rely on legacy to remain relevant forever...
        
           | nox101 wrote:
           | Sounds like COM/DCOM from ~1995. Every API had a public
           | interface including a description. You could open the DCOM
           | Inspector, browse all the APIs, and see the type signature of
           | every function and its docs.
        
             | pjmlp wrote:
             | Still is COM from 2025, given its relevance on Windows,
             | even more since Vista, as all Longhorn ideas were remade in
             | COM.
             | 
             | However the tooling experience is pretty much ~1995, with
             | the difference IDL is at version 3.0.
        
           | Brian_K_White wrote:
           | headers perform the same job for all code, not just code
           | that's in some library.
           | 
           | Frankly your description of what you just called easy sounds
           | terrible and pointlessly extra, indirection that doesn't pay
           | for itself in the form of some overwhelming huge win
           | somewhere else. It's easy only if the alternative was getting
           | it by fax or something.
        
             | saghm wrote:
             | Having to make an entire separate file to mark something as
             | public rather than just having a keyword in the language
             | sounds to me "terrible and pointlessly extra". It's not
             | like you can't just put all your public stuff in it's own
             | file in Rust rather than putting private stuff in it as
             | well; empirically though, people don't do this because it's
             | just not worth the effort.
        
               | nine_k wrote:
               | Think about closed-source software that has to export a
               | low-level programmatic interface. You may not believe it,
               | but it's still widespread.
        
           | nine_k wrote:
           | The include file mechanism is a hack that was acceptable at
           | the time when machines were extremely underpowered, so only
           | the simplest solutions had a chance to be implemented within
           | a reasonable time frame.
           | 
           | By now, of course, precompiled headers exist, but their
           | interplay with #define allows for fun inconsistencies.
           | 
           | And, of course, they leak implementation as much as the
           | author wants, all the way to .h-only single-file libraries.
           | 
           | If you want an example of a sane approach to separate
           | interface and implementation files from last century, take a
           | look e.g. at Modula-2 with its .int and .mod files.
        
             | alextingle wrote:
             | Precompiled headers are a terrible misfeature. I ban them
             | in any code base I am responsible for.
             | 
             | They encourage the use of large header files that group
             | unrelated concerns. In turn that makes small changes in
             | header files produce massive, unnecessary rebuilds of
             | zillions of object files.
             | 
             | The clean practice is to push down #includes into .c files,
             | and to ruthlessly eliminate them if at all possible. That
             | speeds up partial rebuilds enormously. And once you adopt
             | that clean practice, pre-compiled headers yield no benefit
             | anyway.
        
             | pjmlp wrote:
             | Modules already existed in programming languages outside
             | Bell Labs in the same decade, like the Modula-2 you quote.
        
         | kevin_thibedeau wrote:
         | Header files are really a weak hack to deal with resource
         | constrained platforms from the 70s. They only work if you stick
         | to a convention and pale in comparison to languages like Ada
         | with well architected specification for interfaces and
         | implementation without ever needing to reparse over and over
         | again.
         | 
         | I do enjoy using C but that is one area where it should have
         | been better designed.
        
         | billfruit wrote:
         | They are also somewhat of hassle and something not necessary to
         | have.
        
         | ryukoposting wrote:
         | Header files also make it a lot more obvious how you're
         | supposed to distribute a library as a binary, which is good.
        
         | harisund1990 wrote:
         | I love headers but I wish you could split them in two so that
         | private functions and variables can line in the c file. This
         | would help reduce a lot of header bloat as well.
        
           | lzsiga wrote:
           | It is perfectly valid to use more than one header files: some
           | of them can be public (meant to be seen by users of your
           | library), others can be private or internal (only used by
           | your own sources).
        
             | chikere232 wrote:
             | Also, usually it's pretty rare to have things internal to
             | one C file that need explicit prototypes. It's easier to
             | just put things in the right order so the funtion
             | definition etc is before its use.
        
         | m463 wrote:
         | I agree with you, but I don't.
         | 
         | The way C handles header files is sort of "seems-to-work" by
         | just blindly including the text inline.
         | 
         | I know this is not a much-used language, but in comparison, Ada
         | did a pretty nice thing. They have the concept of packages and
         | package bodies. The package is equivalent to the header file,
         | and the package body is the implementation of the package.
         | 
         | I remember (long ago when I used ada) that everyone could
         | compile against the package without having the package body
         | implementation ready so the interfaces could all work before
         | the implementation was ready.
         | 
         | an in another direction, I like how python does "header files"
         | with "import". It maps easily to the filesystem without having
         | to deal with files and the C include file semantics.
        
         | fuzztester wrote:
         | Object Pascal (not the original Pascal) versions like Delphi
         | and Free Pascal have syntax and semantics for _interface_ and
         | _implementation_ sections of the module. Wouldn 't be surprised
         | if Modula-2 and Ada had that too.
        
           | wruza wrote:
           | I remember int/impl sections since the 1990's turbo pascal,
           | which wasn't "object" still, iirc. Also, commercial closed-
           | source units (modules) were often distributed in a .tpu/.dcu
           | + .int form, where .int was basically its source code without
           | the implementation section.
        
             | fuzztester wrote:
             | Interesting.
             | 
             | Yes, I remember the .tpu and .dcu filename extensions.
             | 
             | IIRC, .tpu stood for turbo pascal unit, and .dcu may have
             | meant delphi compiled unit, not sure of the latter.
             | 
             | I don't remember the .int extension, but it would have been
             | there, of course, if you say so.
             | 
             | What was the use of the .int file?
        
               | wruza wrote:
               | It was literally the unit with implementation part just
               | missing. Sort of a header that you can just read(?). Idk
               | if it played a role in compilation, probably not. But
               | some commercial libraries packaged them as well. Here,
               | look at this random repo:
               | https://github.com/keskival/turbo-pascal-
               | experiments/tree/ma... -- few int files at the end of a
               | list.
               | 
               | This page mentions a few ints without any context: https:
               | //comp.lang.pascal.borland.narkive.com/1B3WeJkX/rebuil...
               | 
               | This guy seems to package ints for documentation
               | purposes: https://www.wrotniak.net/hplx/lxtpgr.html
               | 
               | Man this is nostalgic... T-T
        
           | pjmlp wrote:
           | That was inherited from UCSD Pascal, and also incorporated
           | into ISO Extended Pascal, which was supposed to be a more
           | industry friendly revision of ISO Pascal, but by then Object
           | Pascal was the de facto standard.
           | 
           | Modula-2 modules are based on Xerox Mesa, and do have split
           | sections, as does Ada.
           | 
           | Additionally, Modula-2 and Ada modules/packages have a
           | powerful feature that is seldom used, multiple interfaces for
           | the same module implementation, this allows to customise the
           | consume of a module depending on the customer code.
        
         | trenchgun wrote:
         | OCaml .mli interface files are the same, but better.
        
         | pjmlp wrote:
         | Available in most compiled module languages, either separately,
         | Modula-2, Modula-3, Ada, Standard ML, Caml Light, OCaml, F#, D.
         | 
         | Or it can be generated either as text, or graphical tooling,
         | Object Pascal, D, Haskell, Java, C#, F#, Swift, Go, Rust.
         | 
         | All with stronger typing, faster compilation (Rust and Swift
         | toolchain still need some work), proper namespacing.
         | 
         | Unfortunately C tooling has always been more primitive than
         | what was happening outside Bell Labs, and had AT&T been allowed
         | to take commercial advantage, history would be much different,
         | instead we got free lemons, instead of nice juicy oranges.
         | 
         | At least they did come up with TypeScript for C, and it
         | nowadays supports proper modules, alongside bounds checked
         | collection types.
        
         | wruza wrote:
         | I used to think like this, but then I discovered generating
         | (prj_root)/types.d.ts. It doesn't do anything technical because
         | types are in src/**/*, but I do that to generate a quick
         | overview for a project I'm returning to after a month.
         | 
         | Maintaining header files is tedious and I often resorted to a
         | kind of "OBHF.h" for common types, if you know what I mean.
         | Otherwise it's too much cross-tangling and forwards. Even in ts
         | I do type-only src/types.ts for types likely common to
         | everything, mostly because I don't want pages of picky this-
         | from-there this-from-there imports in every module.
         | 
         | As for public/private and sharing "friends" across
         | implementation modules, we didn't invent anything good anyway.
         | I just name my public private symbols impl_foo and that tells
         | me and everyone what it is.
         | 
         | That said, I wouldn't want to make html out of it like these
         | *-doc tools do. Using another program to navigate what is
         | basically code feels like their editor sucks. My position on
         | in-code documentation is that it should be navigatable the same
         | way you write it. External tools and build steps kill
         | "immersion".
        
         | jrmg wrote:
         | I think there may be a difference in thinking that underlies
         | the difference in opinion here.
         | 
         | In my experience, having a header file nudges you to think
         | about interface being a _different thing_ to implementation -
         | something that (because you need to) you think about as more
         | fundamentally separate from the implementation.
         | 
         | Folks who think this way bristle at the idea that interface be
         | generated using tooling. The interface is not an artifact of
         | the implementation - it's a separate, deliberate, and for some
         | even more important thing. It makes no sense to them that it be
         | generated from the implementation source - that's an obvious
         | inversion of priority.
         | 
         | Of course, the reverse is also true - for folks used to auto-
         | generated docs, they bristle at the idea that the interface is
         | not generated from the one true source of truth - the
         | implementation source. To them it's just a reflection of the
         | implementation and it makes no sense to do 'duplicate' work to
         | maintain it.
         | 
         | Working in languages with or without separate interface files
         | nudges people into either camp over time, and they forget what
         | it's like to think in the other way.
        
           | estebank wrote:
           | This thread feels weird to me because when I write code I
           | _do_ think about my public API, have even sketched it out
           | separately looking at the desired usage pattern, but never
           | felt the need to save that sketch as anything other than as
           | part of the documentation. Which lives next to the code that
           | implements that API.
           | 
           | I think it is telling that the handful of languages that
           | still have something akin to .h files use them purely to
           | define cross-language APIs.
        
           | juped wrote:
           | I would generate implementations from interfaces were it
           | possible, but I never want to generate interfaces from
           | implementations.
        
             | kode-tar-gz wrote:
             | Why not?
        
         | thayne wrote:
         | I find it pretty frustrating to have the documentation in a
         | different file from the source code.
         | 
         | When maintaining the code that means I have to go to a separate
         | file to read what a function is supposed to do, or update the
         | documentation.
         | 
         | And when reading the documentation, if the documentation is
         | unclear, I have to go to a separate file to see what the
         | function actually does.
         | 
         | Granted, the implementation can get in the way if you are just
         | reading the documentation, but if you aren't concerned about
         | the implementation, then as others have said, you can use
         | generated documentation.
        
         | Lucasoato wrote:
         | If you want something similar in Python, you could structure
         | your code following the port and adapter pattern. Very
         | effective, especially if paired with hexagonal architecture and
         | type checking libraries like pydantic.
        
         | paulddraper wrote:
         | TypeScript has that.
         | 
         | Although it can infer types and generate the declaration fully
        
       | bjourne wrote:
       | I write unit tests for my C code all that time. It's not
       | difficult if you use a good build system and if you are willing
       | to stomach some boilerplate. Here is one test from my "test
       | suite" for my npy library:                   void
       | test_load_uint8() {             npy_arr *arr =
       | npy_load("tests/npy/uint8.npy");             assert(arr->n_dims
       | == 1);             assert(arr->dims[0] == 100);
       | assert(arr->type == 'u');             npy_free(arr);         }
       | int         main(int argc, char *argv[]) {
       | PRINT_RUN(test_load_uint8);             ...         }
       | 
       | I know I could have some pre-processor generate parts of the
       | tests, but I prefer to KISS.
        
         | TheNewAndy wrote:
         | You will be pleased to know that you are not the only one who
         | does this.
         | 
         | I previously went down the rabbit hole of fancy unit test
         | frameworks, and after a while I realised that they didn't
         | really win much and settled on something almost identical to
         | what you have (my PRINT_RUN macro has a different name, and
         | requires the () to be passed in - and I only ever write it if
         | the time to run all the tests is more than a second or so, just
         | to make it really convenient to point the finger of blame).
         | 
         | The thing that I do which are potentially looked upon poorly by
         | other people are:
         | 
         | 1) I will happily #include a .c file that is being unit tested
         | so I can call static functions in it (I will only #include a
         | single .c file)
         | 
         | 2) I do a tiny preprocessor dance before I #include <assert.h>
         | to make sure NDEBUG is not defined (in case someone builds in a
         | "release mode" which disables asserts)
        
           | wruza wrote:
           | This test/src separation always felt like html/css to me.
           | When still using C, I wrote tests right after a function as a
           | static function with "test_" in the name. And one big run-
           | all-tests function at the end. All you have to do then is to
           | include a c file and call this function. Why would I ever
           | want to separate a test from its subject is a puzzling
           | thought. Would be nice to have "testing {}" sections in other
           | languages too, but in C you can get away with DCE, worst case
           | #ifdef TESTING.
        
             | bjourne wrote:
             | Because tests also serve as api validations. If you can't
             | write a test for functionality without fiddling with
             | internal details the api is probably flawed. Separation
             | forces access via the api.
        
               | wruza wrote:
               | I don't need anything to be "forced" on me, especially
               | when this forcing is nominal and I can #include
               | implementation details. You may need that in teams with
               | absurdly inattentive or stubborn members, but for you-
               | personally it's enough to get the principle and decide
               | when to follow it. The idea is to simply keep tests close
               | to the definitions because that's where the breaking
               | changes happen.
               | 
               |  _If you can 't write a test for functionality without
               | fiddling with internal details the api is probably
               | flawed_
               | 
               | This logic is flawed. If you have an isolated
               | implementation for some procedure that your api invokes
               | in multiple places (or simply abstracted it out for
               | clarity and SoC), it's perfectly reasonable to test it
               | separately even if it isn't officially public.
        
           | PhilipRoman wrote:
           | I agree with all of the above. The only fancy thing which I
           | added is a work queue with multiple threads. There really
           | isn't any pressing need for it since natively compiled tests
           | are very fast anyway, but I'm addicted to optimizing build
           | times.
        
         | WalterBright wrote:
         | Your function looks like it's doing I/O, which won't work at
         | compile time test. Here's an example of a unittest for the
         | ImportC compiler:                   struct S22079         {
         | int a, b, c;         };
         | _Static_assert(sizeof(struct S22079){1,2,3} == sizeof(int)*3,
         | "ok");         _Static_assert(sizeof(struct S22079){1,2,3}.a ==
         | sizeof(int), "ok");
         | 
         | The semantics are checked at compile time, so no need to link &
         | run. With the large volume of tests, this speeds things up
         | considerably. The faster the test suite runs, the more
         | productive I am.
        
           | brabel wrote:
           | Hey Walter, importC is great but on Mac it doesn't work right
           | now because Apple seems to have added the type Float16 to
           | math.h (probably due to this:
           | https://developer.apple.com/documentation/swift/float16) and
           | DMD breaks on that.
           | 
           | Could you have a look at fixing that?
        
             | WalterBright wrote:
             | Aargh. Those sorts of extensions should not be in the
             | system .h file.
        
           | uecker wrote:
           | It is difficult to imagine that compile-time interpretation
           | of tests is faster than compiling and running them for
           | anything more complex. And for trivial stuff it should not
           | matter. Not being able to do I/O is a limitation not a
           | feature.
        
             | WalterBright wrote:
             | Linkers are slow and clunky. Yes, there is a crossover
             | point where executable tests are faster.
        
               | uecker wrote:
               | For one of my C projects (ca. 450 c-files), full rebuild
               | time on my (not super fast) laptop is just below 10
               | seconds (incremental builds < 1s). Compiling and linking
               | all unit tests takes a second, and running all unit tests
               | takes 6-7 seconds. So even running the optimized code for
               | the tests almost doubles the time for rebuilding the full
               | project. Although I like the machine code to be tested
               | that is actually used. (BTW: A single C++ file with
               | templates for CUDA someone added to the project - when
               | activated - almost doubles the build time.)
        
               | pjmlp wrote:
               | What about putting common template parameters into an
               | external template in a binary libray?
        
           | chikere232 wrote:
           | This works in regular C as sizeof() is a constant expression,
           | but perhaps that was your point?
        
         | samiv wrote:
         | I really agree, I think that making the tests as easy as
         | possible to get going goes a long way towards a code base that
         | actually has tests.
         | 
         | I have something very similar.
         | 
         | https://github.com/ensisoft/detonator/blob/master/base/test_...
         | 
         | Borrowed heavily from boost.test.minimal and used to be a
         | single header but but over the years I've had to add a single
         | translation unit!
         | 
         | My takeaway is that if you keep your code base in a condition
         | where tests are always passing you need much less complications
         | in your testing tools and their error reporting and fault
         | tolerance etc. !
        
       | bawolff wrote:
       | I'm not a c programmer, but having unit tests automatically run
       | at compile time seems odd. If i wanted to run tests at the same
       | time as compiling i would put that in the makefile.
        
         | zeroonetwothree wrote:
         | Why is it odd? The compiler does all sorts of other checks
         | based on things like static assert or type information.
        
           | kstenerud wrote:
           | It's odd because you lose control over that aspect of
           | compilation. It slows down the development loop because every
           | time you do a build you have to wait for a bunch of unit
           | tests that you don't care about yet.
           | 
           | Every time you do exploratory work you now have to comment
           | out all the tests that this work breaks because otherwise it
           | won't compile anymore.
           | 
           | That would be even more annoying than Go's stupidly pedantic
           | compiler.
        
             | Alifatisk wrote:
             | > It slows down the development loop because every time you
             | do a build you have to wait for a bunch of unit tests that
             | you don't care about yet.
             | 
             | Can't that be an optional thing decided by some compiler
             | flag? I think I remember doing something like that in D.
        
               | kstenerud wrote:
               | That's how one would normally do it, and that's what I
               | would expect to see: Build for development, and the
               | compiler only errors out if things are so bad that it's
               | unable emit a binary (for everything else, it emits
               | warnings). Build for release, and it errors out unless
               | EVERYTHING is done right.
               | 
               | Unfortunately, ever since golang decided on an autocratic
               | and backwards "there are no warnings, only errors"
               | policy, others have started to sip from the same kool aid
               | jar.
        
         | sixthDot wrote:
         | Most of D users would rather call that "static contracts". I
         | dont know why the author choose to call that "unit test".
        
       | matt3210 wrote:
       | Woah I haven't seen the name "digital mars" since the late 90s
       | looking for compilers!
        
       | thayne wrote:
       | Some of my "obvious things c should do" for me would include
       | things like
       | 
       | - add support for a slice type that encodes a pointer and length
       | 
       | - make re-entrant and ideally threadsafe APIs for things that
       | currently use global state (including environment variables).
       | 
       | - standardize something like defer in go and zig, or gcc's
       | cleanup attribute
       | 
       | - Maybe some portable support for unicode and utf-8.
        
         | zffr wrote:
         | Aren't most of these things you want in the standard library,
         | and not things that the language itself should do?
        
           | thayne wrote:
           | Only half of them. The first and third are language things.
           | 
           | The first could _almost_ be done with macros. Except that
           | separate declarations of an equivalent struct are considered
           | different, so the best you cand do is a macro you can use
           | define your owne typedef for a specific slice type. It could
           | be done in the library if c supported something like a struct
           | that had structural instead of nominal typing.
        
       | DarkmSparks wrote:
       | while this is a very interesting take, I think the premise is a
       | little bit to simple.
       | 
       | Firstly, there are at least 3 C compilers in widespread use, from
       | apple, microsoft and gnu, these are a long way from 1 for 1 to
       | each other so when it says for example:
       | 
       | ->In other words, while C can compute at compile time a simple
       | expression by constant folding, it cannot execute a function at
       | compile time.
       | 
       | Maybe the compiler he tried cannot, but another can, no idea, it
       | wasnt tested, they can be made to (the whole point of the
       | article), apple and microsoft cannot be made to, everything in
       | this article could have have been submitted as a merge request to
       | gnu.... Article doesnt even state whose C compiler they embedded
       | afaict.
       | 
       | wrt to "standard" c specifically, there are for sure some hard
       | constraints on all the wild and wacky hardware support required
       | that must make proposing and implementing changes within the c
       | standard extremely hard, max respect to the anonymous experts
       | that have got it to where it is today, but imho a lot of the "why
       | doesnt c" questions can be as easily answered as "why doesnt V8
       | support 8 bit pic micros".
        
       | kstenerud wrote:
       | Compile time unit tests are as bad of an idea as "unused
       | import/variable/result" errors (rather than warnings). They're
       | "nanny features" that take control away from the developer and
       | inevitably cause you to jump through bureaucratic hoops just to
       | get your work done.
       | 
       | These kinds of build-failing tests are great for your "I think
       | I'm finished now" build, but not for your "I'm in the middle of
       | something" builds (which are what 99% of your builds are).
       | 
       | It's like saying "You can't use the table saw until you put the
       | drill away!"
        
         | omoikane wrote:
         | Maybe these compile time tests are more like `static_assert`,
         | which is valuable for catching incompatible uses of library
         | functions. Pretty good idea in my opinion.
        
           | kstenerud wrote:
           | Sure, enforcing invariants is a good thing to do right off
           | the bat. But not "does this code do what it says on the tin?"
           | kinds of tests. Those are better run gradually, at the
           | (current) developer's behest (and most certainly, not
           | blocking compilation).
        
           | tczMUFlmoNk wrote:
           | The article is literally talking about `_Static_assert`, yes.
           | It's used in the code examples and described in the text.
        
         | chii wrote:
         | > These kinds of build-failing tests are great for your "I
         | think I'm finished now" build, but not for your "I'm in the
         | middle of something" builds
         | 
         | i tend to disagree.
         | 
         | If you tried to express some thought but the compile time tests
         | tells you you're wrong, you might actually just have an
         | incomplete thought, or have not thought through all of the
         | consequences of said expression.
         | 
         | It's basically what type-checking is in haskell - you cannot
         | compile a program that does not type-check correctly. This
         | forces you as a programmer, to always, and only, express
         | complete thoughts. Incomplete, or contradictory thoughts cannot
         | be expressed.
         | 
         | This should, in theory, lead to programs that are more well
         | thought out. It also makes the program harder to write, because
         | it forces the programmer to discover corners of their program
         | for which they "know" isn't valid but don't care.
        
           | kstenerud wrote:
           | > it forces the programmer to discover corners of their
           | program for which they "know" isn't valid but don't care.
           | 
           | And this is precisely why I disagree with forcing it upon the
           | developer at every stage of development. Generally, while in
           | the thick of things, I just want to get things working with
           | one part, not worry about what other parts this breaks (yet).
           | But the pedantic "you have to fix this first" enforcement
           | breaks my concentration because now I have to split my
           | attention to things I don't want to even be bothered with
           | yet. I'll get to it, but I sure as hell don't want you
           | telling me WHEN I should.
        
             | fishstock25 wrote:
             | > because now I have to split my attention to things I
             | don't want to even be bothered with yet.
             | 
             | One of the reasons could that you realize you don't need
             | those parts, so it would have been a waste of time to write
             | tests for them.
             | 
             | Is that the same as saying I don't want to have to write
             | types either? Maybe. Types are like lightweight incomplete
             | specs.
        
               | kstenerud wrote:
               | > One of the reasons could that you realize you don't
               | need those parts, so it would have been a waste of time
               | to write tests for them.
               | 
               | Or perhaps the parts existed, were useful, did have
               | tests, and now a new feature requires refactoring that
               | temporarily breaks things before I finally bring the
               | house in order again. But I don't want to throw out the
               | tests because parts of them may still be useful.
               | 
               | My point is, if the code is capable of being compiled and
               | run, who is anyone to dictate that I shouldn't be allowed
               | to run it (even broken) during my development cycle, just
               | for some bureaucratic "I know better than you" reason?
               | 
               | This is the problem I see all over - people peer out from
               | their limited perspective, assume that they see enough,
               | and then make excessively restrictive policy decisions
               | about what we can and cannot do. It's hubristic and so
               | very, very annoying to the rest of us, especially since
               | they also have a tendency to double-down, and there seems
               | to be no way of getting through to them.
        
             | marcosdumay wrote:
             | > I don't want to even be bothered with yet
             | 
             | Why did you get out of your way to write tests about
             | something that you don't want to be bothered about?
        
               | kstenerud wrote:
               | Because the test already exists as part of the solution
               | that's worked up until now, and I don't want to modify it
               | or any other related tests until I've finished my work to
               | the point that I think the interface is stable enough for
               | the tests to be updated.
        
           | d0mine wrote:
           | Test-driven development has its uses. But it is wrong to make
           | it mandatory. I myself run static checks/unit tests almost
           | all of the time. Still it is useful to skip them from time to
           | time and just run the code to see the results (make it work
           | before you make it "right" according to some linters rules).
        
       | nitwit005 wrote:
       | > Everywhere a C constant-expression appears in the C grammar the
       | compiler should be able to execute functions at compile time,
       | too, as long as the functions do not do things like I/O, access
       | mutable global variables, make system calls, etc.
       | 
       | They have been working on bringing constexpr, which exists in
       | c++, to c. This is essentially a constexpr function.
        
       | WhereIsTheTruth wrote:
       | Another obvious things C should do:
       | 
       | - better enums (with tagged union)
       | 
       | - compile time type introspection
       | 
       | D does the latter (very well btw), but completely missed the mark
       | with enums
        
       | pajko wrote:
       | C23 has constexpr, which cannot be given for functions yet, but
       | there's a proposal: https://www.open-
       | std.org/jtc1/sc22/wg14/www/docs/n2976.pdf
        
       | dailykoder wrote:
       | Maybe C should do that, but it won't. So why complain? Just work
       | with the language as it is
        
       | notorandit wrote:
       | I wish C had a jinja2-like preprocessor!
        
         | chikere232 wrote:
         | You could use whatever preprocessor you like really, just add a
         | make step that translates your template into c. I've seem some
         | crazy person use M4 for example.
         | 
         | It's usually a bad idea, but very feasible
        
       | m463 wrote:
       | The most obvious thing c should do is... evolve.
       | 
       | Even Fortran seems to have added object-oriented constructs, all
       | kinds of new types and concurrent and parallel programming
        
         | 4gotunameagain wrote:
         | There has been an object-oriented evolution of c, it's called
         | c++ ;)
        
         | chikere232 wrote:
         | It has and it does. Compare modern C with c89
         | 
         | It won't become D and you can probably be fairly sure it won't
         | grow a standard garbage collector and object system.
        
       | uecker wrote:
       | I think the real question is why not everybody has already moved
       | to D, if it is so much better and can do all the great things.
       | The answer is that all these things have trade-offs, including
       | implementation effort, changes in tooling, required training,
       | backwards compatibility, etc. some of the features are also not
       | universally seen as better (e.g. IMHO a language which requires
       | forward declaration is better, I also like how headers work).
        
         | nine_k wrote:
         | You mean, of course, Zig and Rust, right?
         | 
         | Because D has a sizable runtime library and GC, which can be
         | opted out of, but with very significant limitations, AFAICT.
        
       | chikere232 wrote:
       | If you want to write D, write D.
       | 
       | C is fine without these things
        
         | bluGill wrote:
         | I want to write d, but I have a ton of c - d makes it easy.
         | rust is harder as I have to write ffi. d makes working with
         | something else easy shich it an advantage. Too bad it never
         | took off.
        
           | Alifatisk wrote:
           | > Too bad it never took off.
           | 
           | It may not be very hyped but the forum and community is quite
           | active. I don't think it's popularity should stop you from
           | exploring it, it's a fascinating language.
        
       | James_K wrote:
       | I feel that much of the point of C is that it's easy to
       | implement. Substantially increasing its scope doesn't seem like
       | the best idea. Perhaps they could do something akin to Scheme and
       | have a "small" and "large" version of the specification.
        
         | oplaadpunt wrote:
         | They have that, to some degree. The standard library is mostly
         | optional. Also, a lot of things are 'implementation defined',
         | so you could just not implement those. That leaves quite a
         | small language core.
        
       | steinuil wrote:
       | > The leaf functions come first, and the global interface
       | functions are last.
       | 
       | To me _that_ is backwards. I prefer code written in a topological
       | order for a number of reasons:
       | 
       | - It mirrors how you write code _within_ a function.
       | 
       | - It's obvious where you should put that function in the module.
       | 
       | - Most importantly, it makes circular dependencies between pieces
       | of code in a module really obvious.
       | 
       | I'm generally not a fan of circular dependencies, because they
       | make codebases much more entangled and prevent you from being
       | able to understand a module as a contained unit. In Python they
       | can even lead to problems you won't see until you run the
       | code[0], but circular imports are probably so common that current
       | type checkers disable that diagnostic by default[1].
       | 
       | I think languages that don't support forward references (C, but
       | also OCaml and SML) let me apply the "principle of least
       | surprise" to circular dependencies. OCaml even disallows
       | recursive dependencies between functions unless you declare the
       | functions with "let rec fn1 = .. and fn2 = ..", which may be a
       | bit annoying while you're writing the code but it's important
       | information when you're reading it.
       | 
       | [0]:
       | https://gist.github.com/Mark24Code/2073470277437f2241033c200...
       | 
       | [1]:
       | https://microsoft.github.io/pyright/#/configuration?id=type-...
       | (see reportImportCycles)
        
       | MichaelMoser123 wrote:
       | i think compile time evaluation should be extended: if it can get
       | a pass over the source code then this could replace preprocessor
       | macros with something less shitty.
        
         | MichaelMoser123 wrote:
         | maybe even add reflection and the ability to check for the type
         | of a value - but that is probably too much to ask for.
        
       | norir wrote:
       | I consider forward references an anti-feature. I want any
       | language I use to have the following property: if I append to the
       | source file, I can't break previously correct code above the
       | insertion point. Forward references both break this property
       | _and_ requires multiple compiler passes.
       | 
       | More generally though, it's time to stick a fork in c. To me the
       | only sane ways to use c are as a compilation target or for quick
       | and dirty prototypes.
       | 
       | We can't make c better by adding to it. We need to let it die
       | peacefully so its grandchildren may live.
        
         | the-grump wrote:
         | C will always live.
         | 
         | I'm not onboard with significant changes to C but the language
         | will always be around at the interface between hardware and
         | software and probably as the lingua franca for FFI.
        
       | sylware wrote:
       | The obvious thing C should do since its syntax is already too
       | rich and complex: create a uC or C- syntax profile which would
       | remove tons of this complexity.
       | 
       | https://news.ycombinator.com/item?id=42657591
        
       | drpixie wrote:
       | > Everywhere a C constant-expression appears in the C grammar the
       | compiler should be able to execute functions at compile time,
       | too, as long as the functions do not do things like I/O, access
       | mutable global variables, make system calls, etc.
       | 
       | That one is easily broken. Pick a function that runs for a
       | lloooonngg time...                 int busybeaver(int n) {...}
       | // pure function returns max lifetime of n state busy beaver
       | machine            int x = busybeaver(99);
        
       ___________________________________________________________________
       (page generated 2025-01-12 23:01 UTC)