Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Let's Destroy C
#ifdef EVIL_COROUTINE
// This lovely hack makes use of switch statements,
// And the __LINE__ C macro
// It tracks the current state, and switches case.
// But... I imagine awful things may happen with an extra semi-colon.
// Which would be hard to debug.
#if defined(EVIL_LAMBDA) && !defined(EVIL_NO_WARN)
// And bad things happen with expression statements.
#warning "Lambda's don't play well with Coroutines. Avoid using them in the body of a coroutine."
#endif
#ifndef EVIL_NO_WARN
#warning "Coroutine's don't support nesting. It may work sometimes, other times it may explode."
#endif
// Original macro hack by Robert Elder (c) 2016. Used against their advice, but with their permission.
#define coroutine() static int state=0; switch(state) { case 0:
#define co_return(x) { state=__LINE__; return x; case __LINE__:; }
#define co_end() }
#endif
#ifndef EVIL_NO_FLOW
// Included by default
#define then ){
#define end }
#define If if(
#define Else } else {
#define For for(
#define While while(
#define Do do{
#define Switch(x) switch(x){
#define Case(x) case x:
#endif
#ifndef EVIL_NO_IO
// The IO Module.
// Included by default. To pretend C is high-level.
// User wants IO, give the all the IO.
#include <stdio.h>
// Yes, Generics. (aka type-switch). It's C11 only,
// but who cares.
// stdint identifiers (inttypes.h) should be catered for by the below.
// Original display_format macro by Robert Gamble, (c) 2012
// used with permission.
// Expanded upon to incorporate const, volatile and const volatile types,
// as they don't get selected for. (static does for obvious reasons).
// Whilst volatile types can change between accesses, technically using a
// _Generic _shouldn't_ access it, but compile to the right choice.
#define display_format(x) _Generic((x), \
char: "%c", \
signed char: "%hhd", \
unsigned char: "%hhu", \
signed short: "%hd", \
unsigned short: "%hu", \
signed int: "%d", \
unsigned int: "%u", \
long int: "%ld", \
unsigned long int: "%lu", \
long long int: "%lld", \
unsigned long long int: "%llu", \
float: "%f", \
double: "%f", \
long double: "%Lf", \
char *: "%s", \
void *: "%p", \
volatile char: "%c", \
volatile signed char: "%hhd", \
volatile unsigned char: "%hhu", \
volatile signed short: "%hd", \
volatile unsigned short: "%hu", \
volatile signed int: "%d", \
volatile unsigned int: "%u", \
volatile long int: "%ld", \
volatile unsigned long int: "%lu", \
volatile long long int: "%lld", \
volatile unsigned long long int: "%llu", \
volatile float: "%f", \
volatile double: "%f", \
volatile long double: "%Lf", \
volatile char *: "%s", \
volatile void *: "%p", \
const char: "%c", \
const signed char: "%hhd", \
const unsigned char: "%hhu", \
const signed short: "%hd", \
const unsigned short: "%hu", \
const signed int: "%d", \
const unsigned int: "%u", \
const long int: "%ld", \
const unsigned long int: "%lu", \
const long long int: "%lld", \
const unsigned long long int: "%llu", \
const float: "%f", \
const double: "%f", \
const long double: "%Lf", \
const char *: "%s", \
const void *: "%p", \
const volatile char: "%c", \
const volatile signed char: "%hhd", \
const volatile unsigned char: "%hhu", \
const volatile signed short: "%hd", \
const volatile unsigned short: "%hu", \
const volatile signed int: "%d", \
const volatile unsigned int: "%u", \
const volatile long int: "%ld", \
const volatile unsigned long int: "%lu", \
const volatile long long int: "%lld", \
const volatile unsigned long long int: "%llu", \
const volatile float: "%f", \
const volatile double: "%f", \
const volatile long double: "%Lf", \
const volatile char *: "%s", \
const volatile void *: "%p", \
default: "%d")
// The main printing function.
#define display(x) printf(display_format(x), x)
#define displayf(f, x) fprintf(f, display_format(x), x)
// Windows has a different line ending.
#if defined(_WIN32) || defined(__WIN32) || defined(WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(__WIN64) || defined(WIN64) || defined(__WIN64__) || defined(__WINNT) || defined(__WINNT__) || defined(WINNT)
#define displayln(x) printf(display_format(x), x); printf("%s", "\r\n")
#define displayfln(f, x) fprintf(f, display_format(x), x); printf("%s", "\r\n")
#else
#define displayln(x) printf(display_format(x), x); printf("%c", '\n')
#define displayfln(f, x) fprintf(f, display_format(x), x); printf("%c", '\n')
#endif
// Basically a _Generic.
#define repr_type(x) _Generic((0,x), \
char: "char", \
signed char: "signed char", \
unsigned char: "unsigned char", \
signed short: "signed short", \
unsigned short: "unsigned short", \
signed int: "signed int", \
unsigned int: "unsigned int", \
long int: "long int", \
unsigned long int: "unsigned long int", \
long long int: "long long int", \
unsigned long long int: "unsigned long long int", \
float: "float", \
double: "double", \
long double: "long double", \
char *: "char pointer", \
void *: "void pointer", \
volatile char: "volatile char", \
volatile signed char: "volatile signed char", \
volatile unsigned char: "volatile unsigned char", \
volatile signed short: "volatile signed short", \
volatile unsigned short: "volatile unsigned short", \
volatile signed int: "volatile signed int", \
volatile unsigned int: "volatile unsigned int", \
volatile long int: "volatile long int", \
volatile unsigned long int: "volatile unsigned long int", \
volatile long long int: "volatile long long int", \
volatile unsigned long long int: "volatile unsigned long long int", \
volatile float: "volatile float", \
volatile double: "volatile double", \
volatile long double: "volatile long double", \
volatile char *: "volatile char pointer", \
volatile void *: "volatile void pointer", \
const char: "const char", \
const signed char: "const signed char", \
const unsigned char: "const unsigned char", \
const signed short: "const signed short", \
const unsigned short: "const unsigned short", \
const signed int: "const signed int", \
const unsigned int: "const unsigned int", \
const long int: "const long int", \
const unsigned long int: "const unsigned long int", \
const long long int: "const long long int", \
const unsigned long long int: "const unsigned long long int", \
const float: "const float", \
const double: "const double", \
const long double: "const long double", \
const char *: "const char pointer", \
const void *: "const void pointer", \
const volatile char: "const volatile char", \
const volatile signed char: "const volatile signed char", \
const volatile unsigned char: "const volatile unsigned char", \
const volatile signed short: "const volatile signed short", \
const volatile unsigned short: "const volatile unsigned short", \
const volatile signed int: "const volatile signed int", \
const volatile unsigned int: "const volatile unsigned int", \
const volatile long int: "const volatile long int", \
const volatile unsigned long int: "const volatile unsigned long int", \
const volatile long long int: "const volatile long long int", \
const volatile unsigned long long int: "const volatile unsigned long long int", \
const volatile float: "const volatile float", \
const volatile double: "const volatile double", \
const volatile long double: "const volatile long double", \
const volatile char *: "const volatile char pointer", \
const volatile void *: "const volatile void pointer", \
default: "Unknown")
// endl, just a symbol that can be used to produce the normal
// line ending.
// endlf can take a file to print to.
// e.g. ```display(x); display(y); endl;```
// ```endlf(FILE* x);```
// Windows has a different line ending.
#if defined(_WIN32) || defined(__WIN32) || defined(WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(__WIN64) || defined(WIN64) || defined(__WIN64__) || defined(__WINNT) || defined(__WINNT__) || defined(WINNT)
#define endl printf("%s", "\r\n")
#define endlf(f) fprintf(f, "%s", "\r\n")
#else
#define endl printf("%c", '\n')
#define endlf(f) fprintf(f, "%c", '\n')
#endif
#endif
#ifdef EVIL_LAMBDA
// This requires nested functions to be allowed.
// Only GCC supports it.
// ... Unconfirmed if Clang does. It might.
#if defined(__clang__) || !defined(__GNUC__)
#error "Lambda requires a GNU compiler."
#endif
// A cleaner, but slightly more cumbersome lambda:
#define lambda(ret_type, _body) ({ ret_type _ _body _; })
// e.g. int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; });
// Pros:
// * Woot, easier to pass, as the user has to know the signature anyway.
// * Name not part of lambda definition. More lambda-y.
// * Body of function inside macro, feels more like a lambda.
// * Uses expression disgnator (GCC-only), which creates a properly constructed function pointer.
// * It *may* work under Clang too!
// Cons:
// * The signature isn't constructed for the user, they have to both know and understand it.
#endif
#ifndef EVIL_NO_MAIN
// Included by default
#define Main int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv
#endif
#ifndef EVIL_NO_PROC
// Included by default
#define declare(_name, _ret, ...) _ret _name(__VA_ARGS__)
#define proc(_name, _ret, ...) _ret _name(__VA_ARGS__){
#endif

Let's Destroy C

I have a pet project I work on, every now and then. CNoEvil.

The concept is simple enough.

What if, for a moment, we forgot all the rules we know. That we ignore every good idea, and accept all the terrible ones. That nothing is off limits. Can we turn C into a new language? Can we do what Lisp and Forth let the over-eager programmer do, but in C?


Some concepts

We're going to point out some definitions in other files - they're too big to inline into a blog post.

You can assume that all of these header definitions get collapsed into a single file, called evil.h.

We won't dwell on many C features. If they're not obvious to you, there's a lot of information at your fingertips to explain them. The idea here isn't to explain how C has moved on. It's to abuse it.


First of all, let's fix up a simple program:

#include <stdio.h>

int main(int argc, char* argv[]) {

  printf("%s\n", "Hello, World!");

}

That's an awful lot of symbolic syntax.

Let's try and get rid of a little of that.

Format

Format specifiers are incredibly useful in C. Allowing you to specify how many decimal places to put after a float, where to use commas when outputting numbers. Whether to use the locale specifier to get the right , or . syntax, etc.

But, for the general case, we don't need it. So we can make it disappear.

We can do this, thanks to a C11 feature, called _Generic, which is sort of like a type-based switch. It'll match against the first compatible type.

If we define display_format as a _Generic switch, like you can see in evil_io.h, then we can replace our printf with a very simple set of defines:

#define display(x) printf(display_format(x), x)
#define displayln(x) printf(display_format(x), x); printf("%s", "\r\n")

Now we can rewrite our program like this:

#include "evil.h"

int main(int argc, char* argv[]) {
	displayln("Hello, World!");
}

There. That's a lot more high level. And it works correctly for a whole bunch of things other than strings, too.

Main

We've got a fairly typical main definition here. But we can do better. We can hide argc and argv, and just assume the programmer knows they're implicitly available. Because there is nothing worse than implicit values.

In fact, we'll also silence the compiler that might complain if we don't end up using them to inspect commandline flags.

#define Main int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv

Unfortunately, just defining our Main isn't enough. We need a couple more defines, which will come in extremely handy in the future. Just a couple symbol replacements.

#define then ){
#define end }

Now. That's better. We can now rewrite our program:

#include "evil.h"

Main then
  displayln("Hello, World!");
end

Brilliant. Now it doesn't look like C. It still compiles like C. In fact, it should compile without warnings.

(Have a glance at evil_flow.h for a few more useful defines that mean we can escape the brace syntax and pretend that C works like Lua's syntax.)

High Level Constructs

We've got a Hello, World that looks simple. It wasn't a hard path to get here. But we can do even better than that.

We can add in things people don't expect to exist in C at all.

Then we can start pretending our poor, abused little program is actually a higher level language than it is. And we haven't even broken any C syntax, which means we can safely and easily link against any other C library, even if it is a header-only library.

Lambda

With a GNU-extension (it may or may not work under other compilers), we can easily write a lambda, and give C the ability to have anonymous functions. We still need to use C's function-pointer syntax, but that doesn't turn out too bad in practice.

#define lambda(ret_type, _body) ({ ret_type _ _body _; })

There! Simple, isn't it? Well, maybe not entirely obvious how it works. (See evil_lambda.h for our full implementation.)

#define EVIL_LAMBDA
#include "evil.h"

Main then
  int (*max)(int, int) = lambda(int,
  	                         (int x, int y) {
  	                         	return x > y ? x : y;
  	                         });

  displayln(max(1, 2));
end

We create a function pointer called max, which returns an int, and takes two int arguments. The lambda assigned to it matches. It returns the bigger of the two values with a simple one-liner.

You use it like you might expect, but max only exists inside main, and is ready to be passed to another function so you can start building up your functional tools.

Coroutines

You can write proper coroutine systems for C. They tend to be big, and complicated and extremely helpful.

But we're doing the wrong thing.

So, apart from emitting some compile-time warnings, the crux of evil_coroutine.h is this magnificent madness:

// Original macro hack by Robert Elder (c) 2016. Used against their advice, but with their permission.
#define coroutine() static int state=0; switch(state) { case 0:
#define co_return(x) { state=__LINE__; return x; case __LINE__:; }
#define co_end() }

By storing state and using a switch as a computer GOTO, you can now write functions that appear to be resumeable.

Like so:

#define EVIL_COROUTINE
#include "evil.h"

int example() {
  static int i = 0;
  coroutine();
  While true then
    co_return(++i);
  end
  co_end();
  return i;
}

Main then
  displayln(example());
  displayln(example());
  displayln(example());
  displayln(example());
  displayln(example());
  displayln(example());
  displayln(example());
  displayln(example());
end

Despite being dangerous, and poorly thought through if you're insane enough to put this anywhere near production code, we are looking like we have coroutines.

Unfortunately, those damn braces are back again.

Procs

Technically speaking, C doesn't have functions. Because functions are pure and have no side-effects, and C is one giant stinking pile of a side-effect.

What C has, is properly known as procedures. So let's reflect that when we redefine how we make them, to get ride of the braces:

#define declare(_name, _ret, ...) _ret _name(__VA_ARGS__)
#define proc(_name, _ret, ...) _ret _name(__VA_ARGS__){

This fits in nicely with our existing then and end macros.

We put the return type right before any listing of arguments, and after the name, which can make it easier when reading over the definition or decleration.

It let's us change the above example into this marvelous little beauty:

#define EVIL_COROUTINE
#include "evil.h"

declare(example, int);

proc(example, int)
  static int i = 0;
  coroutine();
  While true then
    co_return(++i);
  end
  co_end();
  return i;
end

Main then
  displayln(example());
  displayln(example());
  displayln(example());
  displayln(example());
  displayln(example());
  displayln(example());
  displayln(example());
  displayln(example());
end

That's better. It looks more consistent with the rest of our syntax, whilst still not breaking how C works at all.

We've practically abolished symbols in the final syntax. They're still there, but minimal. We haven't introduced any whitespace sensitivity, but we have simplified how it looks. Made it feel like a scripting language.


CNoEvil goes a lot further than this. It adds introspection, a new assert library with it's own stacktrace format, hash routines and so on.

But this is a taste of how well you can screw up the C language with just a handful of overpowered macros.

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 30, 2020

If you want a further look at the horribleness of CNoEvil, then the manual has definitions, examples and all sorts of documentation. Because even a ridiculous thing like it deserves to have pretty documentation.

@Joshua-Ashton

This comment has been minimized.

Copy link

Joshua-Ashton commented Jan 30, 2020

e

@xatier

This comment has been minimized.

Copy link

xatier commented Jan 30, 2020

f

@sharpobject

This comment has been minimized.

Copy link

sharpobject commented Jan 30, 2020

g

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 30, 2020

h?

I don't know what we're doing.

@stochastic-thread

This comment has been minimized.

Copy link

stochastic-thread commented Jan 30, 2020

i

@kyrgyz-nlp

This comment has been minimized.

Copy link

kyrgyz-nlp commented Jan 30, 2020

j

yeah!

@joe-el-khoury

This comment has been minimized.

Copy link

joe-el-khoury commented Jan 30, 2020

k

@snowlove

This comment has been minimized.

Copy link

snowlove commented Jan 30, 2020

l

@rajdude0

This comment has been minimized.

Copy link

rajdude0 commented Jan 30, 2020

m

@AESthetix256

This comment has been minimized.

Copy link

AESthetix256 commented Jan 30, 2020

n

@pyk

This comment has been minimized.

Copy link

pyk commented Jan 30, 2020

...knows they're implicitly available...

implicit is dangerous

@corona10

This comment has been minimized.

Copy link

corona10 commented Jan 30, 2020

o

@ninjapretzel

This comment has been minimized.

Copy link

ninjapretzel commented Jan 30, 2020

Bad ideas?
Whitespace sensitivity that requires spaces in certain areas, and also requires tabs in other specific areas.
There must be a way.

@vsimko

This comment has been minimized.

Copy link

vsimko commented Jan 30, 2020

p

@cbluth

This comment has been minimized.

Copy link

cbluth commented Jan 30, 2020

Qq

@harrybiddle

This comment has been minimized.

Copy link

harrybiddle commented Jan 30, 2020

r

@ZaneH

This comment has been minimized.

Copy link

ZaneH commented Jan 30, 2020

s

@escherize

This comment has been minimized.

Copy link

escherize commented Jan 30, 2020

t

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 30, 2020

...knows they're implicitly available...

implicit is dangerous

@pyk Of course. I call the project "CNoEvil" and all the modules start with "EVIL_". Everything about it is a terrible idea.

The license for the full project has this in it:

  1. The licensee acknowledges that this software is utterly insane in it's nature, and not fit for any purpose.
@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 30, 2020

Bad ideas?
Whitespace sensitivity that requires spaces in certain areas, and also requires tabs in other specific areas.
There must be a way.

@ninjapretzel

I did find a way that required whitespace sensitivity a while back. But... Whitespace sensitivity is an abomination. Even I'm not willing to flay myself with that one.

@antekone

This comment has been minimized.

Copy link

antekone commented Jan 30, 2020

If you use multiple statements in a macro, surround it with do { /* statements */ } while(0), because now this will not work as expected:

if(x)
    displayln("test");

(without { and })

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 30, 2020

If you use multiple statements in a macro, surround it with do { /* statements */ } while(0), because now this will not work as expected:

if(x)
    displayln("test");

(without { and })

@antekone

But this works as expected:

If x then
  displayln("test");
end

If you're mixing C and CNoEvil you need to understand what you're doing anyway. I don't really see the problem.

@skfarhat

This comment has been minimized.

Copy link

skfarhat commented Jan 30, 2020

v
(I wanted to be that guy)

@adamjk

This comment has been minimized.

Copy link

adamjk commented Jan 30, 2020

VV

@wenerme

This comment has been minimized.

Copy link

wenerme commented Jan 30, 2020

x

@agrison

This comment has been minimized.

Copy link

agrison commented Jan 30, 2020

y

@pyr0hu

This comment has been minimized.

Copy link

pyr0hu commented Jan 30, 2020

z

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 30, 2020

We've done it! Whatever that was. Starting at 'e'.

@svkoskin

This comment has been minimized.

Copy link

svkoskin commented Jan 30, 2020

å

@TheMisir

This comment has been minimized.

Copy link

TheMisir commented Jan 30, 2020

b

@BazookaMusic

This comment has been minimized.

Copy link

BazookaMusic commented Jan 30, 2020

φ

@vlttnv

This comment has been minimized.

Copy link

vlttnv commented Jan 30, 2020

ю

@agrison

This comment has been minimized.

Copy link

agrison commented Jan 30, 2020

ε

@mariosal

This comment has been minimized.

Copy link

mariosal commented Jan 30, 2020

ΤΙΝΑΦΤΟΡΕ

@johnnybravo-xyz

This comment has been minimized.

Copy link

johnnybravo-xyz commented Jan 30, 2020

$

@agrison

This comment has been minimized.

Copy link

agrison commented Jan 30, 2020

$

{$}

@madnight

This comment has been minimized.

Copy link

madnight commented Jan 30, 2020

Nice

@AeroNotix

This comment has been minimized.

Copy link

AeroNotix commented Jan 30, 2020

afternoon, Rivs.

@Meithal

This comment has been minimized.

Copy link

Meithal commented Jan 30, 2020

Just saying there is a report abuse button

@Computeiful

This comment has been minimized.

Copy link

Computeiful commented Jan 30, 2020

I think the letters are good suggestions for names for this "Alternative C"

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 30, 2020

I think the letters are good suggestions for names for this "Alternative C"

@Computeiful - It's called CNoEvil. It's a nice way of spelling out the insanity.

@myfreeweb

This comment has been minimized.

Copy link

myfreeweb commented Jan 30, 2020

With clang -fblocks you can have actual closures, not just anonymous functions :)

@elgunhuseynli

This comment has been minimized.

Copy link

elgunhuseynli commented Jan 30, 2020

a

@elgunhuseynli

This comment has been minimized.

Copy link

elgunhuseynli commented Jan 30, 2020

ə

@elgunhuseynli

This comment has been minimized.

Copy link

elgunhuseynli commented Jan 30, 2020

soxum belə vəziyyətin içinə

@sarathyweb

This comment has been minimized.

Copy link

sarathyweb commented Jan 30, 2020

A

@nereu-ls

This comment has been minimized.

Copy link

nereu-ls commented Jan 30, 2020

ç

@Dowwind

This comment has been minimized.

Copy link

Dowwind commented Jan 30, 2020

B

@mmathys

This comment has been minimized.

Copy link

mmathys commented Jan 30, 2020

R

@ZIK1977

This comment has been minimized.

Copy link

ZIK1977 commented Jan 30, 2020

Keybase proof

I hereby claim:

  • I am zik1977 on github.
  • I am limonez1977 (https://keybase.io/limonez1977) on keybase.
  • I have a public key ASCYf5AtvUVr4dJLICrxVlVK9HrrFNKKsRd0MiAyk875TAo

To claim this, I am signing this object:

{
  "body": {
    "key": {
      "eldest_kid": "0120987f902dbd456be1d24b202af156554af47aeb14d28ab1177432203293cef94c0a",
      "host": "keybase.io",
      "kid": "0120987f902dbd456be1d24b202af156554af47aeb14d28ab1177432203293cef94c0a",
      "uid": "9f9e1c3123750d20b190adcaff2ed219",
      "username": "limonez1977"
    },
    "merkle_root": {
      "ctime": 1580384404,
      "hash": "5efb57c6aa393d9f986f77d0aa564be0a9c0340076d1379dade57bd0a599eb871c175b26782cc3c15c12044e7416c1f9287efa8c01a4387c8ed794cd8b05ae8d",
      "hash_meta": "c1f5b968e08e78c6f0600f2f5d72112e3ed586e94f27589f20d9c3076e1580d6",
      "seqno": 14418422
    },
    "service": {
      "entropy": "CNalPPM3TOzXOFdHemU2cozb",
      "name": "github",
      "username": "zik1977"
    },
    "type": "web_service_binding",
    "version": 2
  },
  "client": {
    "name": "keybase.io go client",
    "version": "5.1.1"
  },
  "ctime": 1580384434,
  "expire_in": 504576000,
  "prev": "1a5b1e6e3dbea84561f270a142abeab24ecca3a42a61214762bd4e414caae379",
  "seqno": 16,
  "tag": "signature"
}

with the key ASCYf5AtvUVr4dJLICrxVlVK9HrrFNKKsRd0MiAyk875TAo, yielding the signature:

hKRib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgmH+QLb1Fa+HSSyAq8VZVSvR66xTSirEXdDIgMpPO+UwKp3BheWxvYWTESpcCEMQgGlsebj2+qEVh8nChQqvqsk7Mo6QqYSFHYr1OQUyq43nEIOHnQcgI4m/NGSIbUJYZf3BDjbhlQskTAPPzhj/MPOgPAgHCo3NpZ8RA1cbUgOODW8WQe2NYs6wQnIP1UYSms5z4zT6Re+uE8JHYj3GiVwNm907lcJCAvHXKxIQR+7jGq7saTehmCBypDqhzaWdfdHlwZSCkaGFzaIKkdHlwZQildmFsdWXEIIA2zNRe4ct8asLI1wQhwTzm0Jilg50rju28qGblRmEYo3RhZ80CAqd2ZXJzaW9uAQ==

And finally, I am proving ownership of the github account by posting this as a gist.

My publicly-auditable identity:

https://keybase.io/limonez1977

From the command line:

Consider the keybase command line program.

# look me up
keybase id limonez1977
@BillGR17

This comment has been minimized.

Copy link

BillGR17 commented Jan 30, 2020

ε

@ekarbe

This comment has been minimized.

Copy link

ekarbe commented Jan 30, 2020

ø

@aal89

This comment has been minimized.

Copy link

aal89 commented Jan 30, 2020

f

@Eitz

This comment has been minimized.

Copy link

Eitz commented Jan 30, 2020

What the hell are those comments

@Eitz

This comment has been minimized.

Copy link

Eitz commented Jan 30, 2020

1

@gopalgoel19

This comment has been minimized.

Copy link

gopalgoel19 commented Jan 30, 2020

2

@TheoMcCabe

This comment has been minimized.

Copy link

TheoMcCabe commented Jan 30, 2020

R

@aretas77

This comment has been minimized.

Copy link

aretas77 commented Jan 30, 2020

ą

@mido3ds

This comment has been minimized.

Copy link

mido3ds commented Jan 30, 2020

O

@hmage

This comment has been minimized.

Copy link

hmage commented Jan 30, 2020

O(1)

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 30, 2020

With clang -fblocks you can have actual closures, not just anonymous functions :)

@myfreeweb

Not quite as easily - Clang Blocks introduce a new kind of pointer (can't be used as function pointers), and you have to manually choose when to free it.

@p3k

This comment has been minimized.

Copy link

p3k commented Jan 30, 2020

Я

@vtrpldn

This comment has been minimized.

Copy link

vtrpldn commented Jan 30, 2020

ç

@madjokal

This comment has been minimized.

Copy link

madjokal commented Jan 30, 2020

O(log n)

@ibrahimcesar

This comment has been minimized.

Copy link

ibrahimcesar commented Jan 30, 2020

ø

@marco-fp

This comment has been minimized.

Copy link

marco-fp commented Jan 30, 2020

O(1)

@o0th

This comment has been minimized.

Copy link

o0th commented Jan 30, 2020

O(n)

@evorition

This comment has been minimized.

Copy link

evorition commented Jan 30, 2020

ъ

@vilaca

This comment has been minimized.

Copy link

vilaca commented Jan 30, 2020

40 years too late? https://www.ioccc.org/

@robertodormepoco

This comment has been minimized.

Copy link

robertodormepoco commented Jan 30, 2020

O(no)

@ralsei

This comment has been minimized.

Copy link

ralsei commented Jan 30, 2020

O(h(god))

@kaashmonee

This comment has been minimized.

Copy link

kaashmonee commented Jan 30, 2020

@midhunkrishna

This comment has been minimized.

Copy link

midhunkrishna commented Jan 30, 2020

Screen Shot 2020-01-30 at 8 31 47 PM

@zzag

This comment has been minimized.

Copy link

zzag commented Jan 30, 2020

wq

@Tyncture

This comment has been minimized.

Copy link

Tyncture commented Jan 30, 2020

:wq!

@sarathyweb

This comment has been minimized.

Copy link

sarathyweb commented Jan 30, 2020

@sidbmw

This comment has been minimized.

Copy link

sidbmw commented Jan 30, 2020

sudo rm -rf /*

sudo rm -rf / --no-preserve-root

@memiux

This comment has been minimized.

Copy link

memiux commented Jan 30, 2020

tumblr_96e076c336e6f7454c461bab981c9748_aadf8c2b_540

@caioluders

This comment has been minimized.

Copy link

caioluders commented Jan 30, 2020


@Haoseo

This comment has been minimized.

Copy link

Haoseo commented Jan 30, 2020

Insanity.

@MartensCedric

This comment has been minimized.

Copy link

MartensCedric commented Jan 30, 2020

image

@shadowcat-mst

This comment has been minimized.

Copy link

shadowcat-mst commented Jan 30, 2020

Has anybody written a head-to-head article pitting CNoEvil against BOURNEGOL yet? ISAGN

@bondsbw

This comment has been minimized.

Copy link

bondsbw commented Jan 30, 2020

@diego200052

This comment has been minimized.

Copy link

diego200052 commented Jan 30, 2020

F

@nravic

This comment has been minimized.

Copy link

nravic commented Jan 30, 2020

afternoon

@sarathyweb

This comment has been minimized.

Copy link

sarathyweb commented Jan 30, 2020

@ahopkins

This comment has been minimized.

Copy link

ahopkins commented Jan 30, 2020

א

@nicowilliams

This comment has been minimized.

Copy link

nicowilliams commented Jan 30, 2020

The evil lambda doesn't work. It returns a pointer to stack-allocated data that has been freed by the time the lambda value has been returned. It'd be awesome though.

@amingilani

This comment has been minimized.

Copy link

amingilani commented Jan 30, 2020

ا
I've been waiting for a chance to write Arabic or Urdu letters, online, all my life.

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 30, 2020

The evil lambda doesn't work. It returns a pointer to stack-allocated data that has been freed by the time the lambda value has been returned. It'd be awesome though.

@nicowilliams

You would be wrong.

> gcc -I. examples/lambda.c 
> ./a.out
$ 2

The lambda isn't freed until the end of the scope containing it.

@menox1981

This comment has been minimized.

Copy link

menox1981 commented Jan 30, 2020

This is just a bastardization of C.

@donn

This comment has been minimized.

Copy link

donn commented Jan 31, 2020

Who hurt you, OP

@girng

This comment has been minimized.

Copy link

girng commented Jan 31, 2020

@midhunkrishna omg LOOOOOOL!

@metacritical

This comment has been minimized.

Copy link

metacritical commented Jan 31, 2020

🤦🏼

@nicowilliams

This comment has been minimized.

Copy link

nicowilliams commented Jan 31, 2020

@shakna-israel: huh!

$ cat l.c
#include <alloca.h>
#include <stdio.h>
#include <string.h>

int
main(void) {
  int (*max)(int, int) = ({ int _ (int x, int y) { return x > y ? x : y; } _; });
  printf("%d, %p\n", max(1, 2), (void*)max);
  char *x = alloca(128);
  memset(x, 0, 128);
  printf("%d, %p\n", max(1, 2), x);
  return 0;
}
$ ./a.out
2, 0x7fffd2535ae0
2, 0x7fffd2535a40
$ 

Well, then, ... the only reason not to use this is that GCC/clang end up causing calls to mlock(2) to happen to make the stack executable.

I'm very happy to be wrong about this!! Thanks!

@philpennock

This comment has been minimized.

Copy link

philpennock commented Jan 31, 2020

I think that credit for the co-routine macros properly belongs to Simon Tatham (author of PuTTY), described by him in 2000: https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

@imaami

This comment has been minimized.

Copy link

imaami commented Jan 31, 2020

Now if you could just make CNoEvil code capable of self-modifying itself at runtime...

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 31, 2020

I think that credit for the co-routine macros properly belongs to Simon Tatham (author of PuTTY), described by him in 2000: https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

@philpennock

Though it's similar, it isn't quite the same. In the article where Elder describes it, he does in fact reference that hack you're suggesting.

However, Tatham goes much further and defines coroutines with proper state that can be mixed.

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 31, 2020

Now if you could just make CNoEvil code capable of self-modifying itself at runtime...

@imaami

Well, you can end up with an executable stack with EVIL_LAMBDA, so I guess it already is.

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 31, 2020

@shakna-israel: huh!

$ cat l.c
#include <alloca.h>
#include <stdio.h>
#include <string.h>

int
main(void) {
  int (*max)(int, int) = ({ int _ (int x, int y) { return x > y ? x : y; } _; });
  printf("%d, %p\n", max(1, 2), (void*)max);
  char *x = alloca(128);
  memset(x, 0, 128);
  printf("%d, %p\n", max(1, 2), x);
  return 0;
}
$ ./a.out
2, 0x7fffd2535ae0
2, 0x7fffd2535a40
$ 

Well, then, ... the only reason not to use this is that GCC/clang end up causing calls to mlock(2) to happen to make the stack executable.

I'm very happy to be wrong about this!! Thanks!

@nicowilliams

Basically the scope for the lambda is the containing function, rather than the expression statement.

But generally speaking, nested functions like we do here, absolutely exemplify one of my first statements, and you should run away screaming when you see them:

What if, for a moment, we forgot all the rules we know. That we ignore every good idea, and accept all the terrible ones.

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 31, 2020

Who hurt you, OP

@donn

I did. When I made this.

@jespa007

This comment has been minimized.

Copy link

jespa007 commented Jan 31, 2020

Set C as a new language based on macros is not recommended if you have to use the debugger. It's becomes weird and easily more error-prone than what C is.

@janhec

This comment has been minimized.

Copy link

janhec commented Jan 31, 2020

't is already taken, but I thought oraCle for one sec. You want to be so good that a debugger is, well... not a priority.
But I really don't want to open a can of works triggered by the word "oracle". Let's think matrix, much happier.

@sickfile

This comment has been minimized.

Copy link

sickfile commented Jan 31, 2020

C

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Jan 31, 2020

Set C as a new language based on macros is not recommended if you have to use the debugger. It's becomes weird and easily more error-prone than what C is.

@jespa007

Pretty sure I already pointed out what a terrible idea this is:

What if, for a moment, we forgot all the rules we know. That we ignore every good idea, and accept all the terrible ones.

@protonesso

This comment has been minimized.

Copy link

protonesso commented Jan 31, 2020

Use gnu

@nicowilliams

This comment has been minimized.

Copy link

nicowilliams commented Jan 31, 2020

Speaking of Simon Tatham's C co-routines, there's also async.h.

(I wrote and contributed most of the SSHv2 GSS Key Exchange support in PuTTY, and I must say that working with Simon's co-routines is really quite a pleasure.)

@nicowilliams

This comment has been minimized.

Copy link

nicowilliams commented Jan 31, 2020

BTW, clang definitely does not support lambdas:

$ cat l.c
#include <alloca.h>
#include <stdio.h>
#include <string.h>

int
main(void) {
  int (*max)(int, int) = ({ int _ (int x, int y) { return x > y ? x : y; } _; });
  printf("%d, %p\n", max(1, 2), (void*)max);
  char *x = alloca(128);
  memset(x, 0, 128);
  printf("%d, %p\n", max(1, 2), x);
  return 0;
}
$ cc l.c
$ clang l.c
l.c:7:50: error: function definition is not allowed here
  int (*max)(int, int) = ({ int _ (int x, int y) { return x > y ? x : y; } _; });
                                                 ^
l.c:7:76: error: use of undeclared identifier '_'
  int (*max)(int, int) = ({ int _ (int x, int y) { return x > y ? x : y; } _; });
                                                                           ^
l.c:7:9: error: initializing 'int (*)(int, int)' with an expression of incompatible type 'void'
  int (*max)(int, int) = ({ int _ (int x, int y) { return x > y ? x : y; } _; });
        ^                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 errors generated.
$ 
@nicowilliams

This comment has been minimized.

Copy link

nicowilliams commented Jan 31, 2020

@shakna-israel: I'm surprised that stack allocations in statement-expressions survive the end of the statement expression, that they get promoted to the parent lexical scope. But, too, I'm happy about that.

There are a lot of GCC/clang extensions I would make use of in production code, and that I've seen production code use (e.g., Lustre, which makes extensive use of statement-expressions). But not local functions. I won't abide an executable stack. I'd rather have something more Blocks-like, where there's a new intrinsic and callable type that under the covers is essentially a struct (to be passed by value) of frame pointer and raw function pointer.

@snaidamast

This comment has been minimized.

Copy link

snaidamast commented Jan 31, 2020

All of this has already been accomplished in other languages such as C#, VB.NET, and Java for starters...

@webatxcent

This comment has been minimized.

Copy link

webatxcent commented Jan 31, 2020

While cute, it is hardly novel. Decades ago there was someone who remapped C into Pascal using defines.

@moe123

This comment has been minimized.

Copy link

moe123 commented Jan 31, 2020

@ssokolow

This comment has been minimized.

Copy link

ssokolow commented Jan 31, 2020

@webatxcent Huh. What I remembered was FORTRAN.

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Feb 1, 2020

While cute, it is hardly novel. Decades ago there was someone who remapped C into Pascal using defines.

@webatxcent

That would probably be Bournegol, and unlike this, it was actually used in production in creating the Bourne shell. Which was one of the reasons no one wanted to help with that particular code base. Which had a few more footguns in it, like TRUE being -1.

@shakna-israel

This comment has been minimized.

Copy link
Owner Author

shakna-israel commented Feb 1, 2020

@nicowilliams

Yeah, Clang does it the sensible way. Blocks are the right way to do things. Unfortunately, they return a new pointer type, so mixing them into this horribleness isn't possible.

But, if you really need lambdas... C++ is right there. With more sensible scoping, etc.

@galyathee

This comment has been minimized.

Copy link

galyathee commented Feb 1, 2020

@galyathee

This comment has been minimized.

Copy link

galyathee commented Feb 1, 2020

Maybe we should follow the advice of Scott Meyers ... do not use #define ;) However using well defined macros and structs we can implement C++ like code ... but when I see C++ 17 features, I do not see any good reason to return to C unless I am coding a simple algorithm for MiControllers...

@jspure

This comment has been minimized.

Copy link

jspure commented Feb 5, 2020

Не ну а чо.

@moe123

This comment has been minimized.

Copy link

moe123 commented Feb 5, 2020

@jspure there is a more literal and synthetic way to express that: nice prank; two words in a language that everybody can grasp and experience the meaning i.e what we name self-respect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.