18 ms·
Let's Destroy C
- roywiggins 7y agoAh, finally a modern successor to Bournegol: http://oldhome.schmorp.de/marc/bournegol.html http://oldhome.schmorp.de/marc/bournegol.html
- shakna 7y ago#define TYPE typedef #define STRUCT TYPE struct #define UNION TYPE union Well, that's extremely opinionated. Which I guess is the point. It does seem to add a BASIC-ness to the code. However, when I see landmines like these: #define TRUE (-1) #define FALSE 0 I might just hide instead of touching it.
- stiray 7y agoUh... This is serious landmine... I suggest author to change this.
- shakna 7y agoThis particular code isn't about the post. It's about Bournegol, which doesn't actually exist anymore, and is actually quite hard to track down any examples of.
- kick 7y agoIt's...not, though? The Bourne Shell was released under a free license years ago along with the rest of v7, and it's just a macro hack. Very much still exists, and pretty easy to find. https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh
- thrwaway69 7y agoCan you explain why?
- Tervis 7y agoOn C language an expression which evaluates to any non-zero value is considered as True. So for example this kind of statement would not likely behave as intended: if (TRUE == expression) { ... }
- Koshkin 7y agoI have refused to hire people who did this. (FALSE, too, even though it is safer.)
- shakna 7y agoTo emphasise: This is about Bournegol. Nothing to do with this post. > Can you explain why? Because it isn't how most C libraries expect true/false to be defined. stdbool in C99 standardized things a bit, but before then what was generally accepted was: > true is 1 > false is !true (Often 0 in practice). Which means that any trivial: if(true) { ... } Won't work under Bournegol. Instead you _need_ to compare when doing an if statement. So the C-programmer is easily tripped up. False will work as expected, but True won't... All the time. There may be times it does work. Leaving the programmer throwing their hands in the air.
- thrwaway69 7y agoAh. Thanks for the explanation.
- kaetemi 7y agoMost cases I've seen accept anything as true as long as the LSB is 1, and quite strictly 0 as false. Everything else is up in the air. The value -1 is definitely true. :)
- ufo 7y ago
- kick 7y agoThe author is 76, and retired. This is from UNIX v7, before the standardization of C.
- mst 7y agoDon't bash it til you understand it.
- kick 7y agoIt's an ALGOL-derivative, not a BASIC-derivative. It's trying to be ALGOL. Bourne was one of the few people to write an ALGOL-68 compiler. I might just hide instead of touching it. This code (the Bourne Shell source) is actually the reason why the IOCCC was created.
- mhd 7y agoI know it's bad preprocessor magic, but I kinda liked that. I thought about hacking together a quick "Oberon-ish" to JS "transpiler" for a while, of course going along with a decidedly Wirth-ian code style. I might call it "werenotwirthy". But there's also the appeal of the dreaded Incunabulum...
- awalias 7y agothe best thing about this was discovering https://git.sr.ht/ https://git.sr.ht/
- shakna 7y agoThe extremely flexible todo, and build parts of the site are probably my favourites. Anonymous non-user people allowed on issue tracker that doesn't have to be linked to any repo? Awesome. (And you can export easily.) Easy to combine multiple build projects into a single build that can be kicked off by any one of the projects? Sweet. Drew is also really responsive if you run into any problems.
- yjftsjthsd-h 7y agoPart of what makes sr.ht great is that it's made of tools that can be used together or separately. If you want to use it for just task tracking, great. If you want to use it for task tracking, git repo, and CI/CD, that's fine. If you want to use it for task tracking on a project that keeps code on github and does CI on gitlab... It still works great:)
- trashcan 7y agoI love the simple and lightweight interface, but everything is hard to discover. Sourcehut? Sounds great- I'd love to replace my Gogs/gitlab instance with something more lightweight. Let's download the source and run it. I guess click on "git" on https://git.sr.ht/ https://git.sr.ht/? Wait that's where I already am with no indication that that is the selected tab. Ok maybe the link for sourcehut? https://sourcehut.org/ https://sourcehut.org/ Cool. There's some links about pricing, ignore that and click on "100% free and open source software" Now I am just at a list of what appears to be about 20 repos, all with helpful names like sr.ht-apkbuilds. I can tell that this person has put a ton of work into making something that is probably fantastic, but it is really all presented in an undiscoverable way. I still have no idea what language this project is written in, how to deploy or maintain it. I assume https://git.sr.ht/~sircmpwn/git.sr.ht https://git.sr.ht/~sircmpwn/git.sr.ht might be what I want, but it still looks like the inscrutable mess that reminds me of hgweb. There's not even a readme on the first page, let alone the source or anything useful. There's a link to https://man.sr.ht/git.sr.ht/installation.md https://man.sr.ht/git.sr.ht/installation.md. Which looks like it might be what I want, but I guess I'm old and at this point I've lost interest. I'm definitely being crotchety, but I wish this information was organized in a more useful way. I can tell Drew has put a lot of time into it, but I don't feel like it is being shared in an effective way. And I would definitely never pay for software like this. Please someone tell me I'm crazy and this UI makes perfect sense to them.
- cperciva 7y agoWhile I'm not a fan of the specific changes shown in this article, I do agree with the principle of "remove boilerplate when possible". I found that the largest amount of boilerplate I ever wrote was in main() to parse the command line, and to that end I wrote a "magic getopt", which handles both short and long options in-line without external tables (https://www.daemonology.net/blog/2015-12-06-magic-getopt.html https://www.daemonology.net/blog/2015-12-06-magic-getopt.htm... ); and PARSENUM, which handles the common cases of parsing human-readable values while appropriately checking for overflow (https://github.com/Tarsnap/libcperciva/blob/master/util/parsenum.h https://github.com/Tarsnap/libcperciva/blob/master/util/pars...).
- kazinator 7y ago> Windows has a different line ending But not when printing to stdio.h text streams; \n turns into the right line ending.
- shakna 7y ago> But not when printing to stdio.h text streams; \n turns into the right line ending. Only on some, but not all, compilers. I got varying behaviour until I went ahead and did it myself.
- chrisbrandow 7y agoReminds me a lot of how the first version of objective-C was implemented
- kccqzy 7y agoGNU lambdas are incredibly evil. In order for lambdas to capture their environment and yet still be available as a function pointer, the compiler makes the stack executable and stores the trampoline code on the stack. C++ lambdas don't suffer from this problem.
- shakna 7y agoGNU doesn't really have lambdas. This is an abuse of the compound statement macro. IIRC compound statements, like I've presented, don't use trampolines. Nested functions definitely do, but that isn't quite what we're doing here. GCC _should_ compile using descriptors for the compound statements that lambda is expanding to instead of using trampolines. gcc -fno-trampolines -I. examples/lambda.c Works. There's no trampoline present.
- newnewpdro 7y agoWhat's `readelf -l a.out` say for GNU_STACK flags? I'd try myself but my gcc doesn't recognize -fno-trampolines.
- shakna 7y agoRW
- PatrickMDev 7y ago#define lambda(ret_type, _body) ({ ret_type _ _body _; }) I found this one really fascinating, they're using both statement expression and nested functions. Their (reformatted) example of: int (*max)(int, int) = lambda(int, (int x, int y) { return x > y ? x : y; }); macro-expands to int (*max)(int, int) = ({ int _(int x, int y) { return x > y ? x : y;}; }); So they have a statement-expression with just one statement in it - the value of that statement is the value of the whole statement-expression. And the one statement inside the statement expression is a declaration of a nested function named _. Statement-expressions decay their return types, so that function gets converted to a function pointer. And thus your "lambda" is a pointer to a GCC nested function.
- pininja 7y agoGreat writing and clever hacking!
- tus88 7y agoThis hurt my brain.
- newnewpdro 7y agoIt hurt my eyes.
- ipsum2 7y agoThis is amazing. Are there any larger examples that uses more of the functionality of the library that I could read?
- shakna 7y agoWhilst it uses an older version of CNoEvil, and I haven't updated it yet, the largest application is probably evilshell [0]. There's also all the examples [1]. [0] https://git.sr.ht/~shakna/evilshell https://git.sr.ht/~shakna/evilshell [1] https://git.sr.ht/~shakna/cnoevil3/tree/master/examples https://git.sr.ht/~shakna/cnoevil3/tree/master/examples
- mst 7y agoI've already said this a couple other places but I really wanted to say: "This is amazing and it would be hilarious if you wrote a CNoEvil vs. BOURNEGOL head to head article". <3
- _pmf_ 7y agoYou invented Ada.
- ffwff 7y agoWith enough macros you can even bring high-level programming to C a la libcello: http://libcello.org/ http://libcello.org/
- artsr 7y agoWhen I started learning C++, there was't a C++ compiler available for my Atari ST (late 80s), there was only Borland C. So instead I used the C preprocessor to emulate classes, virtual functions etcetera. Not everything could be implemented this way, but it looked like C++ close enough for me to learn it.
- shakna 7y agoThat's basically how C++ was originally created, or the prototype, "C with Classes", albeit with a custom preprocessor. [0] > In October of 1979 I had a pre− processor, called Cpre, that added Simula− like classes to C run-ning and in March of 1980 this pre− processor had been refined to the point where it supported onereal project and several experiments. Recognising that you could do it that way is kinda awesome. Maybe not entirely uncommon, but you drafted a powerful bit of software yourself. [0] http://www.stroustrup.com/hopl2.pdf http://www.stroustrup.com/hopl2.pdf
- gpderetta 7y agoThanks, awesome paper (which remind me that I should read the ARM at some point). Interesting stuff: "Cfront [the fist c++ compiler] was (and is) a traditional compiler front− end performing a complete check of the syntax and semantics of the language" "[...] the C compiler is used as a code generator only. [...]. I stress this because there has been a long history of confusion about what Cfront was/is. It has been called a preprocessor because it generates C, and for people in the C community (and elsewhere) that has been taken as proof that Cfront was a rather simple program – something like a macro preprocessor." Cfront being a preprocessor is something that gets repeated often. As you correctly point out, cpre was the preprocessor and it wasn't realy C++ yet.
- userbinator 7y agoI believe the "modern" term for Cfront would be "transpiler".
- 7y ago
- archivist1 7y agoThat's a work of art!
- 0xdead 7y agoPerfect usage of the title to get on HN frontpage.
- non-entity 7y agoI really expected this to be another aggressive Rust marketing peice at first
- shakna 7y agoWell, thanks to Rust's more powerful macro system, I'm sure someone could butcher it even more than the C-PreProcessor lets me do to C, here.
- nothrabannosir 7y agoBig fan. I don’t see support for exceptions, maybe this can help: https://github.com/hraban/c-exceptions https://github.com/hraban/c-exceptions (I ended up using this in a job once. They didn’t hire me back...)
- shakna 7y agoThat's horrifying. I've opened a ticket to implement it. [0] [0] https://todo.sr.ht/~shakna/CNoEvil3/9 https://todo.sr.ht/~shakna/CNoEvil3/9
- notacoward 7y agoAgreed, exceptions would fit right in with all the other "people think they're great but they're really terrible" ideas in the OP.
- vparikh 7y agoA really good example of this is the implementation of Objective-C. I believe even the original C++ implementation by Bjarne Stroustrup were also C macros.
- senozhatsky 7y ago> printf("%s\n", "Hello, World!"); > > That's an awful lot of symbolic syntax. Well... Because it should have been printf("Hello, World!\n"); in the first place? One can do something like printf("%s,%s%c\n", "Hello", "World", '!'); and claim that C is awful and that displayln("Hello, World!"); is so much better.
- tsbinz 7y agoThis works for a literal, but the author was probably thinking of "print a string that comes from somewhere else" where you do the first thing to avoid format characters to be interpreted.
- senozhatsky 7y agoCould be. But wouldn't then displayln() require the same string placeholder? Be it "%s", or "{1}" or someting else.
- shakna 7y agodisplayln uses the massive _Generic in display_format to supply a large number of formatters automatically. [0] [0] https://gist.github.com/shakna-israel/4fd31ee469274aa49f8f9793c3e71163#file-evil_io-h-L20 https://gist.github.com/shakna-israel/4fd31ee469274aa49f8f97...
- senozhatsky 7y agoI see. So when for a char pointer one needs to do printf("%p\n", v); the Generic call must look like this displayln((const void *)v); is it really better?
- shakna 7y agoOr you could have the pointer be a pointer before you feed it to the function. void* v; v = ...; ... displayln(v);
- dnpp123 7y agoI guess this article is to be taken lightly as kind of a "joke" ? The first proposed macro displayln is the archetype of malpractice. What if I want to do: if(some_condition) displayln("enjoy debugging that"); etc etc... almost all proposed changes seem to be awful ?
- arianvanp 7y agoIt's a form of humour called sarcasm. It even has a convenient hint at the top to announce that it is "Let's destroy C"
- shakna 7y agoI think I was pretty explicit: > What if, for a moment, we forgot all the rules we know. That we ignore every good idea, and accept all the terrible ones.
- dnpp123 7y agoOw alrighty then. Looking at comments here I'm not sure some other people saw that sentence haha
- steerablesafe 7y agoI guess that's one of the reason of the do { /.../ } while(false) pattern in macros.
- samatman 7y agoYou've done a fine job of elucidating one of the many reasons why responsible codebases require braces for single-statement if clauses.
- wruza 7y agoMy friend served as a military officer and told me a story. Every evening before lights out, he had to count soldiers via a roll call, a 10-minute routine. If someone coughed during that (no matter what intent), then suddenly many soldiers began to cough, and that ruined everything. The only solution he figured out to remain legal/moral was to pause and start it all over if someone coughs.
- MaxBarraclough 7y agoWouldn't it make sense to roll-call different sub-groups independently?
- progx 7y agoReturn of Turbo Pascal ;-)
- taneq 7y agoDidn't one version of the "Ten Commandments of C" have a rule stating "Thou shalt not use the preprocessor to turn C into a different language"? :P
- honestoHeminway 7y agolets go even further down the simplification way- and turn to lua.. function xy (arg) end looks like this xy z - so you are one typo away from a function call to nil & narnia without ever hearing about it.. life on the bleeding edge of sketch..
- 32gbsd 7y agoI hope we get some new stuff out of this. The world is lacking in new stuff because everyone is in the business of creating slavery apis.
- davidhyde 7y agoThis reminds me of Arduino. evil.h == arduino.h
- shakna 7y agoI've used CNoEvil in a couple Arduino projects. It makes about as much sense when something goes wrong as with the Arduino library.
- garaetjjte 7y ago// Original macro hack by Robert Elder (c) 2016 Described also back in 2000 on https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
- juped 7y agoAnd as Simon Tatham points out there, Tom Duff invented it in the 80s, but only hinted at it rather than publish it.
- macintux 7y agoI haven't looked closely at either, but the spirit of this effort reminds me of Cello, recently discussed here: https://news.ycombinator.com/item?id=22102533 https://news.ycombinator.com/item?id=22102533
- bjornjajajaja 7y agoC has its place; every language has its quirks. I think a lot of emphasis needs to be put on testing code. Like SQLite uses Tcl to script tests. C is easy to test because the “software units” are only structures and functions.
- Koshkin 7y ago> C has its place No kidding.
- shakna 7y ago> C has its place; every language has its quirks. I think a lot of emphasis needs to be put on testing code. The library that this post is a subset of, actually does have a testsuite. It compiles every example, and it's own documentation. The testsuite still needs to grow, and check evaluations rather than whether something "works", but it's getting there.
- bregma 7y agoThis just in: you can write bad code in any language. In this case, that language is the C Preprocessor, already well documented and widely accepted as a Bad Language To Start With. The title of the article is misleading: it's not "destroying C" it's writing Bad Code using the C Preprocessor. It's not destroying C any more than a series of bad puns destroys the English language.
- chapium 7y agoIf one has the time, I think its a good exercise to turn a paradigm on its head and see if anything good falls out of the awfulness.
- mumblemumble 7y ago> you can write bad code in any language. But it's more fun to do in C.
- tatersolid 7y agoIt’s much easier to do it in C, even when you think you’re not writing bad code.
- shakna 7y ago> This just in: you can write bad code in any language. Pretty sure I pointed that out: > Can we turn C into a new language? Can we do what Lisp and Forth let the over-eager programmer do, but in C? --- > The title of the article is misleading: it's not "destroying C" it's writing Bad Code using the C Preprocessor. To put it another way, when I look at something like this, I might moan and say, "They've ruined it." It's an expression.
- Koshkin 7y agoIn some subtle way C was already "destroyed" with the introduction of C89. The K&R C had simplicity and beauty to it. Include files were small as they only contained the essential stuff (mostly definitions of structs). UNIX version 7 - the last "true" edition of UNIX - including its entire userspace - was still written in it. X Window system was written in it. Vi was written in it. Now all people do is complain.
- rramadass 7y ago>Now all people do is complain Amen, brother! For a language that has been so supremely successful in the "real world", i simply don't understand the HN crowd's disdain for it. As an aside, "printf format strings" are actually a DSL and hence the complexity. In their book, The Practice of Programming Kernighan & Pike actually show how to implement something similar for Network Packet encoding/decoding. It is quite neat and validates the power of this approach.
- thrower123 7y agoI can't help but be reminded of some of the horrifying basic.h and pascal.h macro-filled headers I can remember from long, long ago. Except people actually used those...
- agentultra 7y agoThose defines remind me of how Bourne shell was originally written using such a system to make the source look more like Algol 68. It might even still be maintained that way in BSD to this day [0]. [0] https://books.google.ca/books?id=9f9uAQAAQBAJ&pg=PA9&lpg=PA9&dq=expert+c+programming+bourne+shell&source=bl&ots=UJafm8jP8B&sig=ACfU3U1Nb-leW-01XLRyLB6xsKOBrJf3hw&hl=en&sa=X&ved=2ahUKEwi_6dDe0KvnAhVuRN8KHQDTBgUQ6AEwAHoECAoQAQ#v=onepage&q=expert%20c%20programming%20bourne%20shell&f=false https://books.google.ca/books?id=9f9uAQAAQBAJ&pg=PA9&lpg=PA9...
- mst 7y agoYes! Has anybody written a head-to-head article pitting CNoEvil against BOURNEGOL yet? ISAGN
- JdeBP 7y ago"to this day" in 1994 is very much not "to this day" in 2020. The various BSDs use the Almquist and Korn shells for /bin/sh nowadays. Indeed, M. Van der Linden was out of date even back in 1994. At that time, BSD had already been largely freed of AT&T code, such as the Bourne shell, for 3 years. It's now approaching 3 decades.
- agentultra 7y agoI've forgotten how old that book is. Geez time flies. Thanks for the clarification!
- dilippkumar 7y agoI've been a C programmer for several years now (out of necessity - C is still the best language for firmware and embedded development) There's been a lot of noise on the internet about Rust and C++20 and "modern" languages. I recently had an opportunity to try some of these first hand. Honestly the new language features are all terrible with few exceptions. In general, anything that was added to a language to support generics or to hide pointers and memory management from the developers on a language that isn't a lisp has only produced more harm than good. I grew up as a programmer always thinking about the underlying CPU and the underlying memory and how my code would interact with both of them. It requires greater care as a programmer and tools like static analysers, code reviews and rigorous testing are extremely important. Improving these tools and coming up with new ones is far more useful (in my opinion) than trying to update C. Yes C has it's flaws. Sometimes, those flaws are important and a replacement language is useful. I find that Go is a very interesting replacement for C at sufficiently higher up the stack. Something like Haskell probably will best fill in the gaps between C and Go. I'm not convinced that fixing C with something like C++ was a good idea. Similarly, anything that's trying to fix C++'s problems is unlikely to come up with a decent way to have generics and hide memory and pointers from developers. If I was going to create my own language, I'd keep it like C, remove some of the undefined behaviors, add support for Posits, 128 bit and arbitrarily wide signed and unsigned integers, features for explicit cache management (!!!), container classes as part of a standard library, Go-lang style interfaces and bake in something like cppcheck into the compiler. /rant
- metalliqaz 7y agoI feel the same way you do, but I still want to spend more time getting to know Rust.
- SuspiciousSwan 7y agoEven a subset of that would be amazing to me -- "removed all of the undefined behaviors, added 128 bit signed and unsigned integers, features for explicit cache management (!!!), and container classes as part of a standard library"
- lhweroiuwer 7y ago
- ggerules 7y agoWell there is the International Obfuscated C Code Contest.... Details here.... https://www.ioccc.org/ https://www.ioccc.org/
- kragen 7y agoThe IOCCC was inspired by Steve Bourne using precisely this kind of tricks in the C source for the Bourne Shell to make C look like Algol-60.
- deleted 7y ago[deleted]
- deleted 7y ago[deleted]
- exabrial 7y agoI really don't think the result is better than the beginning.
- Pete_D 7y agoOff topic, but in today's bitter and divided world, it's reassuringly wholesome that people can still get together to recite the alphabet in GitHub comments.
- justincredible 7y agoIt's more like the alphabet++ at this point.
- gricardo99 7y agoReminds me of Arthur Whitney's k.h[1] Example: // remove more clutter #define O printf #define R return #define Z static #define P(x,y) {if(x)R(y);} #define U(x) P(!(x),0) #define SW switch #define CS(n,x) case n:x;break; #define CD default 1 - https://github.com/KxSystems/kdb/blob/master/c/c/k.h https://github.com/KxSystems/kdb/blob/master/c/c/k.h
- samatman 7y agoReminded more strongly of the Bourne shell macros which turned C into some awful Algol dialect: https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/mac.h https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd...
- spopejoy 7y agoOr the J incunabulum[1] ... typedef char C;typedef long I; typedef struct a{I t,r,d[3],p[2];}*A; #define P printf #define R return #define V1(f) A f(w)A w; #define V2(f) A f(a,w)A a,w; #define DO(n,x) {I i=0,_n=(n);for(;i<_n;++i){x;}} I *ma(n){R(I*)malloc(n*4);}mv(d,s,n)I *d,*s;{DO(n,d[i]=s[i]);} tr(r,d)I *d;{I z=1;DO(r,z=z*d[i]);R z;} A ga(t,r,d)I *d;{A z=(A)ma(5+tr(r,d));z->t=t,z->r=r,mv(z->d,d,r); R z;} V1(iota){I n=*w->p;A z=ga(0,1,&n);DO(n,z->p[i]=i);R z;} V2(plus){I r=w->r,*d=w->d,n=tr(r,d);A z=ga(0,r,d); DO(n,z->p[i]=a->p[i]+w->p[i]);R z;} ... 1 - https://code.jsoftware.com/wiki/Essays/Incunabulum https://code.jsoftware.com/wiki/Essays/Incunabulum
- ubertakter 7y agoThe end result reminds me of Julia. Enough to make me wonder if there's something like it underneath Julia somewhere, or if that's how it started (unfortunately I don't have time to really dig in to it right now).
- JabavuAdams 7y agoI used to be really interested in new languages and compilers. Now ... let's just get on with making Machine Programmers / CASE tools. I don't want to be futzing around with individual lines of code anyway. I guess I want to be like a very technical PM.
- MaxBarraclough 7y agoI'm reminded of a brilliant talk, Can I Has Grammar? [0][1], which does similar violence to C++. [0] https://youtu.be/tsG95Y-C14k?t=277 https://youtu.be/tsG95Y-C14k?t=277 [1] https://news.ycombinator.com/item?id=18832953 https://news.ycombinator.com/item?id=18832953