This is obviously terrible, but my favorite part of this whole thing is the fact that he included the parentheses in the IF/THEN macros, so you didn't have to do it for the condition in the if statement. So, like, this line doesn't need parentheses around the condition:
IF (n=to-from)<=1 THEN return FI
All modern languages that try and do a refresh on the C style (Rust and Swift notably) do this, it's clearly the right idea. They should just update the C and C++ syntax to make those parentheses optional at this point.
(PS. some people would also recoil at the assignment-as-expression used in that line, but that's just good clean fun!)
They already make them optional for single nested statements, which causes a gigantic mess when you extend code and forget to add them.
if (a)
doThis();
andThat();
is interpreted as
if(a){
doThis();
}
andThat();
Some compilers are nice enough to throw around misleading indentation warnings, but without an explicit block termination like FI this just causes issues all over the place.
>
If you look closely, those aren't angle brackets, they're characters from the Canadian Aboriginal Syllabics block, which are allowed in Go identifiers. From Go's perspective, that's just one long identifier.
The thread goes downhill fast from there to the point where
> I
once wrote a short Swift program with every identifier a different length chain of 0-width spaces.
Yet another pre-processor-to-victory post. Check the header in the source.
If we're going to use crazy header files, I want to see someone get the linux kernel to build and boot while including this: https://gist.github.com/aras-p/6224951
A long time ago, a friend who was studying mathematics at the time, approached me laughing hysterically and showed me a page in an "intro to C"-style book. It showed an example of how one would write a "get circumference of a circle" function. At the top of the code, there was a #define for the value of pi.
The text describing the code said something like this about why pi is #defined and not included directly in the expression:
"We define pi as a constant for two reasons: 1) it makes the expressions using it more readable 2) should the value of pi ever change, we will only have to change it in one place in the code"
As funny as it sounds, there is still a bit of truth in there, though: code might change from using floats to double for example, so you might want to replace single-precision constants by double-precision constants. Only need to replace a single pi constant in that case. :)
There is a similar joke in the book Learning Perl, 3rd Edition by Randal L. Schwartz and Tom Phoenix. I wrote a post about it here fourteen years ago: https://susam.net/blog/from-perl-to-pi.html . Quoting from the book:
"It's easier to type $pi than 𝜋, especially if you don't have Unicode. And it will be easy to maintain the program in case the value of ever changes."
There is also a comment by Randal Schwartz in the comments section where he credits Tom Phoenix with that particular bit of humour.
Oh my the defines. What is language and semantics?
"When I use a word," Humpty Dumpty said, in a rather scornful tone, "it means just what I choose it to mean - neither more nor less."
Very ugly, ill-defined, uncodified democracy. No one tells you when you vote, no one counts up the votes, there are no official results.
When you speak or communicate with someone else, you are voting with them. "I vote this word means X, for a poor/crude/coarse agreement of what X is the first place."
And this is propaganda at its finest. Evil doublespeak: say one thing, and it actually is another. Snuck in.
I was under the impression it was "#define CNAME value" – what does it mean when there is no value? A trip to Google didn't turn up anything for me, so I'm wondering if a C master can weigh in. Thanks!
Walter, D has conditional compilation, versioning and CTFE without preprocessor so I guess that covers the 99% "sane" functionality. Where do you draw the line between that and the 1% abomination part, i.e. your thoughts on, say, compile time type introspection and things like generating ('printing') types/declarations?
The abomination is using the preprocessor to redefine the syntax and/or invent new syntax. Supporting identifier characters that look like `:` is just madness.
Of course, I've also opined that Unicode supporting multiple encodings for the same glyph is also madness. The Unicode people veered off the tracks and sank into a swamp when they decided that semantic information should be encoded into Unicode characters.
One other thing that would be great that sometimes people use the preprocessor for is having the names variables/enums as runtime strings. Like, if you have an enum and a function to get the string representation for debug purposes (i.e. the name of the enum as represented inside the source code):
you can use various preprocessor tricks to implement getEnumName such that you don't have to change it when adding more cases to the enum. This would be much better implemented with some compiler intrinsic/operator like `nameof(val)` that returned a string. C# does something similar with its `nameof`.
> With some simple improvements to the language, about 99% of the C preprocessor use can be abandoned and deprecated.
Arguably the C feature most used in other languages is the C preprocessor's conditional compilation for e.g. different OSes. Used by languages from Fortran (yes, there exists FPP now - for a suitable definition of 'now') to Haskell (yes, `{-# LANGUAGE CPP #-}`).
In C++, anyway. C’s expressiveness, on the other hand, is pretty weak, and a preprocessor is very useful there.
A better preprocessor (a C code generator, effectively) would be a simple program that would interpret the <% and %> brackets or similar (by “inverting” them). It is very powerful paradigm.
You're talking about metaprogramming. I've seen C code that does metaprogramming with the preprocessor.
If you want to use metaprogramming, you've outgrown C and should consider a more powerful language. There are plenty to pick from. DasBetterC, for example.
Back when I had just learned Pascal, and was beginning to learn C, I did some of this. No idea why I thought that would make it easier to learn. I did not take it as far as the author of this article did. But I did expand it to function calls like "#define writeln printf". Looking back, I'm a bit amazed I managed to learn it, as I was obviously putting more work into not learning C than learning it.
Meaning classes, algebraic data types, pattern matching, boxed objects, iterators and garbage collection. All they need is smart pointers or a borrow checker and it'd practically be C++ or Rust, except it's rather brittle because it's just a bunch of macros.
> People have experimented with it, but there is no high profile project I know of that uses it. Cello is too big and scary a dependency for new C projects if they want to be portable and easy to maintain.
There was the "Val Linker"[1] (also for the Amiga, though I can't seem to find that version), written in a kind of Pascal-ish C, powered by macros..
Snippet:
void get_order_token()
BeginDeclarations
EndDeclarations
BeginCode
While token_break_char Is ' '
BeginWhile
order_token_get_char();
EndWhile;
copy_string(token, null_string);
If IsIdentifier(token_break_char)
Then
While IsIdentifier(token_break_char)
BeginWhile
concat_char_to_string(token, token_break_char);
order_token_get_char();
EndWhile;
lowercase_string(token);
Else
If token_break_char Is '['
Then
While token_break_char IsNot ']'
BeginWhile
concat_char_to_string(token, token_break_char);
order_token_get_char();
EndWhile;
order_token_get_char();
If case_ignore.val
Then
lowercase_string(token);
EndIf;
Else
concat_char_to_string(token, token_break_char);
order_token_get_char();
EndIf;
EndIf;
return;
EndCode
I once saw a C header that defined "BEGIN" as {, "END" as } and other pascalisms. I find it difficult to understand how some people are so stubborn to change their model of thinking.
I believe I’ve seen stuff like this used as partial help when transcribing a program from pascal or Fortran to C, from before the era of automatic tooling to help.
Whether they’d ever go back and finish the migration is not known.
The biggest pitfall with manual bulk transcribing Pascal to C back in day, was that operator precedence between both languages are really different. Not only is their model of thinking different, it is also wrong.
> Caught me. I had recently heard that Arthur Whitney, author of the A+, k, and q languages (which are array programming languages like APL and J), would use the C preprocessor to create his own language and then write the implementation of his language in that self-defined language.
Stephen Bourne did this in the sources of the Bourne shell, around 1977, to make C look like Algol.
It's interesting, first time I wrote C was after learning programming through Java. My "C" code was all new_<type>(..) .. I couldn't not think in Java syntax.
I once wrote a set of macros to emulate the syntax of Oberon, and then used that to write some code that could later be easily converted to real Oberon. It was a fun exercise - highly recommended.
1) Write CPP macro language to emulate Oberon syntax.
2) Write program in the above macro language.
3) This program looks like Oberon but we now have two ways of "compiling" it; a) Feeding it to the CPP/C Compiler or b) Feeding it to the Oberon compiler directly with a little bit of tweaking.
In the 80's I worked for a guy who insisted that we wrote all our C using macros that made it look like FORTRAN, amongst much other nonsense. How fondly I remember the many hilarious hours spent trying to pin down the cause of unexpected results.
I don't remember any specific examples, but consider:
My thoughts exactly. It's just C with a slightly different syntax. Would be way more interesting if it was using lazy evaluation, or maybe some other kind of term rewriting, possibly with garbage collection or some smart miniature region based memory management.
> would use the C preprocessor to create his own language and then write the implementation of his language in that self-defined language
Yeah that sounds like the easiest way to make your colleagues hate you
I "love" how we had more languages in the 70s (usually created as a one-off project for people with not so much user friendliness in mind) think m4, awk, tcl, etc
Terraform module args used to be very limited, and I didn’t know how to generate JSON it would take instead of HCL, so I actually used m4 to avoid repeating every template n times. And now we are sad because of course Terraform has improved quite a bit.
I mean we do have a lot of (perhaps too many) markdown dialects today.
Wikipedia, wordpress, github, stackexchange, you name it.
Last time I was using a Q&A forum for calculus course,
it uses $$ to start and close a MathJax div.
What do you like about it? I don’t think it needs to be stated why the majority of people here probably hate it, but I am curious why anyone would actively like it. I can maybe see that there’s a sense of achievement in being able to grok a codebase that is often described as unreadable
Steve Bourne used a set of macros to enable ALGOL-like programming in C and used it to implement his Unix shell - https://research.swtch.com/shmacro
This is obviously terrible, but my favorite part of this whole thing is the fact that he included the parentheses in the IF/THEN macros, so you didn't have to do it for the condition in the if statement. So, like, this line doesn't need parentheses around the condition:
All modern languages that try and do a refresh on the C style (Rust and Swift notably) do this, it's clearly the right idea. They should just update the C and C++ syntax to make those parentheses optional at this point.
(PS. some people would also recoil at the assignment-as-expression used in that line, but that's just good clean fun!)
They already make them optional for single nested statements, which causes a gigantic mess when you extend code and forget to add them.
is interpreted as
Some compilers are nice enough to throw around misleading indentation warnings, but without an explicit block termination like FI this just causes issues all over the place.
15 replies →
ha, I was googling FORTRAN C macros and went nowhere, that was the page I was looking for :)
Lennart Augustsson once wrote the least-Haskell Haskell program, amazingly without the help of a preprocessor:
https://augustss.blogspot.com/2009/02/regression-they-say-th...
And to answer the question in the top comment:
https://hackage.haskell.org/package/BASIC-0.1.5.0/docs/Langu...
cpaint.h
There was a reddit thread about crafty Unicode usage in programming a few years ago.
https://www.reddit.com/r/rust/comments/5penft/parallelizing_...
> If you look closely, those aren't angle brackets, they're characters from the Canadian Aboriginal Syllabics block, which are allowed in Go identifiers. From Go's perspective, that's just one long identifier.
The thread goes downhill fast from there to the point where
> I once wrote a short Swift program with every identifier a different length chain of 0-width spaces.
Checkout this Arthur Whitney (Genius Language Designer/Programmer) thread - https://docs.google.com/document/d/1W83ME5JecI2hd5hAUqQ1BVF3...
Yet another pre-processor-to-victory post. Check the header in the source.
If we're going to use crazy header files, I want to see someone get the linux kernel to build and boot while including this: https://gist.github.com/aras-p/6224951
> #define M_PI 3.2f
A long time ago, a friend who was studying mathematics at the time, approached me laughing hysterically and showed me a page in an "intro to C"-style book. It showed an example of how one would write a "get circumference of a circle" function. At the top of the code, there was a #define for the value of pi.
The text describing the code said something like this about why pi is #defined and not included directly in the expression:
"We define pi as a constant for two reasons: 1) it makes the expressions using it more readable 2) should the value of pi ever change, we will only have to change it in one place in the code"
As funny as it sounds, there is still a bit of truth in there, though: code might change from using floats to double for example, so you might want to replace single-precision constants by double-precision constants. Only need to replace a single pi constant in that case. :)
There is a similar joke in the book Learning Perl, 3rd Edition by Randal L. Schwartz and Tom Phoenix. I wrote a post about it here fourteen years ago: https://susam.net/blog/from-perl-to-pi.html . Quoting from the book:
"It's easier to type $pi than 𝜋, especially if you don't have Unicode. And it will be easy to maintain the program in case the value of ever changes."
There is also a comment by Randal Schwartz in the comments section where he credits Tom Phoenix with that particular bit of humour.
1 reply →
This #define would have almost been required by law in Indiana: https://en.wikipedia.org/wiki/Indiana_Pi_Bill
1 reply →
Depends if you're using biblical pi
This is just too good. It must be a joke on the author's part :D
7 replies →
That #define is making fun of this:
https://www.forbes.com/sites/kionasmith/2018/02/05/indianas-...
But why is it then 3.2f instead of 3.1? Seeing as 3.14 gets rounded to 3.1. Or is that part of the joke and the reason for why the value nee changing?
2 replies →
Oh my the defines. What is language and semantics?
"When I use a word," Humpty Dumpty said, in a rather scornful tone, "it means just what I choose it to mean - neither more nor less."
Very ugly, ill-defined, uncodified democracy. No one tells you when you vote, no one counts up the votes, there are no official results.
When you speak or communicate with someone else, you are voting with them. "I vote this word means X, for a poor/crude/coarse agreement of what X is the first place."
And this is propaganda at its finest. Evil doublespeak: say one thing, and it actually is another. Snuck in.
Fantastic.
Related: https://github.com/Droogans/unmaintainable-code
I don't quite understand these lines:
I was under the impression it was "#define CNAME value" – what does it mean when there is no value? A trip to Google didn't turn up anything for me, so I'm wondering if a C master can weigh in. Thanks!
It defines those to a value of "", effectively stripping them from the source code.
The most common place where this sort of thing occurs is the common idiom
which allows you to sprinkle debug() calls through your code and have them disappear if you compile without defining the macro DEBUG.
1 reply →
Can I do it with an #ifndef?
> I use lots of characters that look like ASCII but are in fact not ASCII but nonetheless accepted as valid identifier characters.
Clever, I was wondering how the : was done, but it's an abomination :-/
With some simple improvements to the language, about 99% of the C preprocessor use can be abandoned and deprecated.
Walter, D has conditional compilation, versioning and CTFE without preprocessor so I guess that covers the 99% "sane" functionality. Where do you draw the line between that and the 1% abomination part, i.e. your thoughts on, say, compile time type introspection and things like generating ('printing') types/declarations?
The abomination is using the preprocessor to redefine the syntax and/or invent new syntax. Supporting identifier characters that look like `:` is just madness.
Of course, I've also opined that Unicode supporting multiple encodings for the same glyph is also madness. The Unicode people veered off the tracks and sank into a swamp when they decided that semantic information should be encoded into Unicode characters.
19 replies →
To clarify, what is needed are:
1. static if conditionals
2. version conditionals
3. assert
4. manifest constants
5. modules
I occasionally find macro usages that would require templates, but these are rare.
One other thing that would be great that sometimes people use the preprocessor for is having the names variables/enums as runtime strings. Like, if you have an enum and a function to get the string representation for debug purposes (i.e. the name of the enum as represented inside the source code):
you can use various preprocessor tricks to implement getEnumName such that you don't have to change it when adding more cases to the enum. This would be much better implemented with some compiler intrinsic/operator like `nameof(val)` that returned a string. C# does something similar with its `nameof`.
5 replies →
> With some simple improvements to the language, about 99% of the C preprocessor use can be abandoned and deprecated.
Arguably the C feature most used in other languages is the C preprocessor's conditional compilation for e.g. different OSes. Used by languages from Fortran (yes, there exists FPP now - for a suitable definition of 'now') to Haskell (yes, `{-# LANGUAGE CPP #-}`).
In C++, anyway. C’s expressiveness, on the other hand, is pretty weak, and a preprocessor is very useful there.
A better preprocessor (a C code generator, effectively) would be a simple program that would interpret the <% and %> brackets or similar (by “inverting” them). It is very powerful paradigm.
You're talking about metaprogramming. I've seen C code that does metaprogramming with the preprocessor.
If you want to use metaprogramming, you've outgrown C and should consider a more powerful language. There are plenty to pick from. DasBetterC, for example.
3 replies →
Back when I had just learned Pascal, and was beginning to learn C, I did some of this. No idea why I thought that would make it easier to learn. I did not take it as far as the author of this article did. But I did expand it to function calls like "#define writeln printf". Looking back, I'm a bit amazed I managed to learn it, as I was obviously putting more work into not learning C than learning it.
It was practically a rite of passage back in the days when Pascal and Pascal-like languages were common to do this with C....
There's also https://libcello.org/ a popular (?) macro-heavy library which makes C feel modern.
"modern" meaning less explicit?
Meaning classes, algebraic data types, pattern matching, boxed objects, iterators and garbage collection. All they need is smart pointers or a borrow checker and it'd practically be C++ or Rust, except it's rather brittle because it's just a bunch of macros.
Have you ever seen it used in the wild?
Their FAQs say that's unlikely:
> Is anyone using Cello?
> People have experimented with it, but there is no high profile project I know of that uses it. Cello is too big and scary a dependency for new C projects if they want to be portable and easy to maintain.
There was the "Val Linker"[1] (also for the Amiga, though I can't seem to find that version), written in a kind of Pascal-ish C, powered by macros.. Snippet:
*1 https://ftp.sunet.se/mirror/archive/ftp.sunet.se/pub/simteln...
Hasn't everyone done at least something similar to this? I'm surprised, I re-define C quite often when I'm bored.
I once saw a C header that defined "BEGIN" as {, "END" as } and other pascalisms. I find it difficult to understand how some people are so stubborn to change their model of thinking.
I believe I’ve seen stuff like this used as partial help when transcribing a program from pascal or Fortran to C, from before the era of automatic tooling to help.
Whether they’d ever go back and finish the migration is not known.
The biggest pitfall with manual bulk transcribing Pascal to C back in day, was that operator precedence between both languages are really different. Not only is their model of thinking different, it is also wrong.
Well, pasting the code into VS Code ruined some of the fun, since the non-ASCII homoglyphs are now highlighted by default.
> Caught me. I had recently heard that Arthur Whitney, author of the A+, k, and q languages (which are array programming languages like APL and J), would use the C preprocessor to create his own language and then write the implementation of his language in that self-defined language.
Stephen Bourne did this in the sources of the Bourne shell, around 1977, to make C look like Algol.
Here it is in V6 Unix, 1979:
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...
If I remember correctly, the first Bourne Shell was written in a Pascal-ish C.
It's interesting, first time I wrote C was after learning programming through Java. My "C" code was all new_<type>(..) .. I couldn't not think in Java syntax.
I once wrote a set of macros to emulate the syntax of Oberon, and then used that to write some code that could later be easily converted to real Oberon. It was a fun exercise - highly recommended.
Do you mean this?
1) Write CPP macro language to emulate Oberon syntax.
2) Write program in the above macro language.
3) This program looks like Oberon but we now have two ways of "compiling" it; a) Feeding it to the CPP/C Compiler or b) Feeding it to the Oberon compiler directly with a little bit of tweaking.
Have i understood it correctly?
Yes, you have.
Once I macro'd `const auto` to `let` in my C++ program. After a few moments of "haha C++ go rust", I got terrified and undid it.
let is now a thing in C++, but not const auto, so probably good that you undid it :-)
In header file's line 12, is there two different semicolons? Is one of them actually semicolon-looking another Unicode character?
(https://github.com/ibara/cpaint/blob/21c70acba373df932920d5f...)
U+FF1B Fullwidth semicolon. The semicolon in CJKV ideographs.
Yep. Been there. Done that.
In the 80's I worked for a guy who insisted that we wrote all our C using macros that made it look like FORTRAN, amongst much other nonsense. How fondly I remember the many hilarious hours spent trying to pin down the cause of unexpected results.
I don't remember any specific examples, but consider:
#define SQ(v) v*v
int sq = SQ(++v);
Classic pitfall with any type of text based pre processor. All variables inside need excessive amount of parenthesis.
In similar vein there is also the “do {} while 0” trick to allow macros to be appear like normal functions and end with semicolon.
Don’t even want to imagine how many more hacks would be needed to transform into another syntax using macros only.
I do like the style of kona if I need an example to confuse people about how C looks like, https://github.com/kevinlawler/kona/tree/master/src
That is the commonly accepted way to write k interpreters after all. ngn/k looks more like k than C too.
https://codeberg.org/ngn/k/src/branch/master/a.c
I’m so glad I don’t have to work with that code, I would lose my mind
A “least-C” needs lazy evaluation at a minimum :-)
My thoughts exactly. It's just C with a slightly different syntax. Would be way more interesting if it was using lazy evaluation, or maybe some other kind of term rewriting, possibly with garbage collection or some smart miniature region based memory management.
> would use the C preprocessor to create his own language and then write the implementation of his language in that self-defined language
Yeah that sounds like the easiest way to make your colleagues hate you
I "love" how we had more languages in the 70s (usually created as a one-off project for people with not so much user friendliness in mind) think m4, awk, tcl, etc
Awk is actually great. M4 not so much.
Some absolute lunatic solved this year's Advent of Code in m4; it was impressive.
Terraform module args used to be very limited, and I didn’t know how to generate JSON it would take instead of HCL, so I actually used m4 to avoid repeating every template n times. And now we are sad because of course Terraform has improved quite a bit.
1 reply →
I mean we do have a lot of (perhaps too many) markdown dialects today. Wikipedia, wordpress, github, stackexchange, you name it. Last time I was using a Q&A forum for calculus course, it uses $$ to start and close a MathJax div.
My fave is Jira, where they have one syntax when creating an issue and another for editing it
Well, at least that is the obvious way to delimit a math div, isn't it?
Would you consider doing string processing in C rather than in Awk or Tcl?
It is funny question because I think most languages get string processing right. Pascal gets it right.
Except C.
1 reply →
In FORTRAN, thank you.
(It was a long time ago, and there was no C compiler on our IBM/370...)
TCL is basically THE string processing language... because everything is a string :p.
For short scripts, awk is nice, but most people would use Python nowadays, and die hard Unix greybeards will use Perl or TCL depending on the mood.
> Yeah that sounds like the easiest way to make your colleagues hate you
Well I'm not Whitney's colleague but I really like his code.
What do you like about it? I don’t think it needs to be stated why the majority of people here probably hate it, but I am curious why anyone would actively like it. I can maybe see that there’s a sense of achievement in being able to grok a codebase that is often described as unreadable
6 replies →