11 ms·
Obvious things C should do
- chikere232 2y agoIf you want to write D, write D. C is fine without these things
- bluGill 2y agoI 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 2y ago> 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.
- _lvbh 2y agoIt really reads like the author just wants Zig.
- convolvatron 2y agoor maybe even D
- WalterBright 2y agoZig copies features from D!
- koolba 2y agoImitation is the sincerest form of flattery.
- WalterBright 2y agoYup. 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 2y agoDo 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 2y agoThe 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 2y ago> 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 2y agoThanks a lot for your answer, I have another one, what languages or topics do you follow for inspiration regarding concepts ?
- WalterBright 2y agoI 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 2y ago
- throwawaymaths 2y agomore 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 2y agoIt keeps adding D features anyway, like constexpr.
- throwawaymaths 2y agothe 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 https://youtu.be/Gv2I7qTux7g?si=p0kVhtB56GvVLLNr
- WalterBright 2y agoD 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 2y ago>> 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 2y ago
- robterrell 2y agoThe 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 2y agoahh, that explains the ImportC comparisons. Also didn't realize that person is also a regular on HN as well.
- TinkersW 2y agoC++ 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 2y agoNobody 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 2y agoRobert Frost would approve
- pjmlp 2y agoOffice has been migrating to modules, there are talks on the matter, and blog entries, https://devblogs.microsoft.com/cppblog/integrating-c-header-units-into-office-using-msvc-1-n/ https://devblogs.microsoft.com/cppblog/integrating-c-header-... https://devblogs.microsoft.com/cppblog/integrating-c-header-units-into-office-using-msvc-2-n/ https://devblogs.microsoft.com/cppblog/integrating-c-header-... https://devblogs.microsoft.com/cppblog/integrating-c-header-units-into-office-using-msvc-3-n/ https://devblogs.microsoft.com/cppblog/integrating-c-header-...
- dccsillag 2y agoIf 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.
- chacham15 2y agoWhile 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 2y ago> 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.
- ryukoposting 2y agoYeah... 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 2y agoJust 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 2y ago> 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.
- kreco 2y ago> 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 2y ago> 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 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 2y ago> 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?
- foul 2y agoNot again, not another D thread noooooooooooooooooo (i'm joking)
- WalterBright 2y agoAll your base belong to D!
- EuAndreh 2y agoGood 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 2y agoI have my own list of things that could "easily" be added to C, but I'd rather them not to be.
- WalterBright 2y agoYou get them anyway in the form of extensions.
- EuAndreh 2y agoThanks, but no thanks.
- WalterBright 2y ago> 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.
- pansa2 2y ago> 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 https://stackoverflow.com/a/73131538
- almostgotcaught 2y agoYou'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 2y ago> 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?
- almostgotcaught 2y ago[flagged]
- pansa2 2y agoYou're right, it's not the same mechanism. But in this particular context it has the same effect - it allows you to define functions in a source file in arbitrary order.
- 2y ago
- dang 2y ago[stub for offtopicness]
- ranger_danger 2y agologin-walled
- deleted 2y ago[deleted]
- _lvbh 2y agoLooks like a new feature. Articles don't render render on xcancel
- smitty1e 2y agoHave a go at this => https://t.co/KXRE5XvuoP https://t.co/KXRE5XvuoP I used the https://publish.twitter.com https://publish.twitter.com thing against the Xeet, then lifted the first HREF out of the embed goo.
- dwattttt 2y agoCurious. I don't have an account, and I can read it.
- ciupicri 2y agoTry https://www.digitalmars.com/articles/Cobvious.html https://www.digitalmars.com/articles/Cobvious.html
- TheNewAndy 2y agoHeader 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 2y agoSome other languages have equivalents (OCaml comes to mind), but usually they’re less necessary
- Lvl999Noob 2y agoI 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 2y agoThat 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.
- bjourne 2y agoI 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 2y agoYou 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 2y agoThis 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 2y ago
- bawolff 2y agoI'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 2y agoWhy is it odd? The compiler does all sorts of other checks based on things like static assert or type information.
- kstenerud 2y agoIt'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 2y ago> 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 2y agoThat'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.
- 2y ago
- matt3210 2y agoWoah I haven’t seen the name “digital mars” since the late 90s looking for compilers!
- thayne 2y agoSome 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 2y agoAren’t most of these things you want in the standard library, and not things that the language itself should do?
- thayne 2y agoOnly 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 2y agowhile 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 2y agoCompile 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 2y agoMaybe 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 2y agoSure, 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 2y agoThe article is literally talking about `_Static_assert`, yes. It's used in the code examples and described in the text.
- chii 2y ago> 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.
- nitwit005 2y ago> 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 2y agoAnother 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 2y agoC23 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 https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2976.pdf
- dailykoder 2y agoMaybe C should do that, but it won't. So why complain? Just work with the language as it is
- m463 2y agoThe 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 2y agoThere has been an object-oriented evolution of c, it's called c++ ;)
- elcritch 2y agoWhat? Nah C++ is some template based language. The real object-oriented evolution of C is Objective-C. ;)
- chikere232 2y agoIt 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.
- notorandit 2y agoI wish C had a jinja2-like preprocessor!
- chikere232 2y agoYou 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
- notorandit 2y agoIntegrated preprocessor. That was what I meant. Maybe with c-syntax awareness.
- uecker 2y agoI 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 2y agoYou 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.
- James_K 2y agoI 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 2y agoThey 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.
- pjmlp 2y agoThat is long gone, when looking at C23 and the myriad of compiler extensions.
- steinuil 2y ago> 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/2073470277437f2241033c2003f98358 https://gist.github.com/Mark24Code/2073470277437f2241033c200... [1]: https://microsoft.github.io/pyright/#/configuration?id=type-check-rule-overrides https://microsoft.github.io/pyright/#/configuration?id=type-... (see reportImportCycles)
- MichaelMoser123 2y agoi 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 2y agomaybe even add reflection and the ability to check for the type of a value - but that is probably too much to ask for.
- norir 2y agoI 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 2y agoC 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.
- me-vs-cat 2y agoWhat do you mean by "break previously correct code"? Can you give an example of what you're thinking with original code and appended code, where concatenating the two (1) does not change the behavior of the original code when forward references are not allowed, and (2) changes the behavior of the original code when forward references are allowed?
- norir 2y agoin d: import std.stdio; alias string = immutable(char)[] void main() { bar(foo()); } void bar(string s) { writeln(s); } string foo() { return "foo"; } now change the definition of foo: import std.stdio; alias string = immutable(char)[]; void main() { bar(foo()); } void bar(string s) { writeln(s); } int foo() { return 1; } This now fails to compile at line 3 even though the breaking change was made at line 5.
- me-vs-cat 2y agoI am unable to fit that example in this prior description: "if I append to the source file, I can't break previously correct code above the insertion point". Can you help me by providing the "previously correct code" and the "appended code" which causes the break after being appended?
- sylware 2y agoThe obvious thing C should do since its syntax is already too rich and complex: create a µC or C- syntax profile which would remove tons of this complexity. https://news.ycombinator.com/item?id=42657591 https://news.ycombinator.com/item?id=42657591
- drpixie 2y ago> 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);
- chikere232 2y agoI tried D last year. As I recall it, there's at least one obvious things from C that D should add: Switches can't seem to figure out that I have a case for every value of the enum I'm switching on, so I need a pointless default: assert(0); I didn't check if this also breaks me getting warnings if I add new values to the enum but not the switch, but I imagine it does
- acuozzo 2y agoThe things I want most from a new C standard: 1. Fix the integer promotion rules. Obeying them as they are now makes code considerably less readable. 2. Choose one of the idiomatic approaches to type punning and make it standard. Having to use memcpy() over and over again is terrible. 3. Make casting a pointer-to-struct to a pointer to its first member supported. Stop leaving it to POSIX. 4. Make the exact-width integer types in stdint.h required. They were brazen enough to require support for long long in C99, so why not? 5. Make integer literals without type suffices be compile-time bignums.
- bretcindy 2y ago[dead]
- ninalanyon 2y agoObvious things C should do is not the same as things that C should obviously do. For instance you have to declare things in the right order in F# too and that seems to be mostly regarded as a modern language.