[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)