Weird Syntax Tricks

Started by
8 comments, last by Servant of the Lord 9 years, 9 months ago

So I was looking to see if any languages supported overloading a function by return type and found this answer.

The bottom of the post contained this snippet of code that fakes overloading by return type in c++.


struct func {
   operator string() { return "1";}
   operator int() { return 2; }
};

int main( ) {
   int x = func(); // calls int version
   string y = func(); // calls string version
   double d = func(); // calls int version
   cout << func() << endl; // calls int version
   func(); // calls neither
}

While I would never use this hack in a real project, it does make a fun exercise in understanding c++.

What other examples of strange language hacks do you know of?

My current game project Platform RPG
Advertisement

Huh, I don't know where I would use that functionality, but it was interesting to see. Anyone see any legitimate uses for it?

Some of these are more of syntax quirks.

The way to fake (kind of) ternary operator in Lua is with and and or:


x = a and b or c

if a is true it'll return b (if it's not false), if a is false it will return c, this is because of Lua quirk - it returns first of the values that 'made the result sure' (think 'which expression would C evaluate last') not just 'true' or 'false' from and and or. It's a bit screwy and I might got it wrong so when in doubt, consult: http://lua-users.org/wiki/TernaryOperator ;)

Also a small syntax quirk of Lua - semicolons are optional but useful if you want to trigger an error in a lua prompt to clear the unfinished chunk but they can be used instead of commas in tables which is a bit screwy too (it's a feature according to http://www.lua.org/manual/5.2/manual.html#9).

Examples here:

http://ideone.com/Da4009

Another funny syntax quirk is that trying to concatenate something to literal number will produce 'malformed number' error if you put the operator just after the literal like so: 1..2

Spacing rarely matters in Lua like that.

Something that can be considered actual trick is that single string literal or a single table can be passed to function call without ().

One neat trick I've seen using that to write a function call to class function as 'class "Foo" ' in a custom class system when defining a new class, it really doesn't look like function call but an element of language if written that way.

Another 'trick' is calling methods on a literal string, it is possible but it needs a () wrapping to prevent an error:


print(("%d"):format(123))

I'm rather partial to Duff's Device. I was positive it wouldn't compile the first time I saw it. tongue.png


switch(count % 8) {
case 0:	do {	*to = *from++;
case 7:		*to = *from++;
case 6:		*to = *from++;
case 5:		*to = *from++;
case 4:		*to = *from++;
case 3:		*to = *from++;
case 2:		*to = *from++;
case 1:		*to = *from++;
	} while(--n > 0);
}

Interweaving a while statement with a switch statement is just weird. blink.png

"This code forms some sort of argument in [the switch-case fall-through] debate, but I'm not sure whether it's for or against." - Duff

THIS POST is a fun read on the subject of language torture .

I cannot remember the books I've read any more than the meals I have eaten; even so, they have made me.

~ Ralph Waldo Emerson

I found this a while ago. Suppose you had several C++ functions f1, f2, etc.. that returned an int and needed to call them in a switch case based on an argument (please, no comments on how this could be done better with such and such C++ feature, this is a contrived example as the thread is about unusual syntax and where it might be used). The natural way to do this would be:

static int f1(/* params */) {
    return 42;
}
 
static int f2(/* params */) {
    return 50;
}

/* ... */

static int test(int arg, /* params */) {
    switch (arg) {
        case 1:
            return f1(/* params */);
        case 2:
            return f2(/* params */);

        /* ... */
    }

    return error;
}
Good so far. Now what if the functions returned void instead, and instead acted on their parameters? Some people would be inclined to do it this way, because, after all, void functions return nothing:

static void test(int arg, /* params */) {
    switch (arg) {
        case 1:
            f1(/* params */);
            break; /* (or return) */
        case 2:
            f2(/* params */);
            break;

        /* ... */
    }
}
But it turns out you can actually do it the same way as before, avoiding the two-line call+break style:

static void test(int arg, /* params */) {
    switch (arg) {
        case 1:
            return f1(/* params */);
        case 2:
            return f2(/* params */);

        /* ... */
    }
}
Which is perfectly legal according to the C++ standard, and does what you expect. In short, you can return a void type in C++. It is debatable whether this makes the code more readable (personally I like it) but I thought it was a neat trick either way. Please note this is not valid in C and should probably result in a warning, usually with -pedantic for gcc and clang.

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”

I like returning void because it allows more expressive code that says (at least to me) 'return whatever this function returns, it'll do the rest of the job, I'm done'. This is just a case when this happens to be 'nothing'(void) so just the 'it'll do the rest of the job, I'm done' part is communicated.

I think it's in C++ because of some templates motivated reason.

Also it might let optimizer use a tail call easier (because compiler/optimizer sees that 'I'm done, let this function take over' part too instead of looking forward, seeing if there is a break after call, if the switch has nothing after itself and so on).

Another trick:

C++ syntax to take a reference to an array. It has use in a neat trick (see below) and restricts input to just right size arrays but it's horrible in itself already:


void takearr(int (&arr)[2]) {}

It will NOT work without () around &arr, it'll think you want to pass in array of references then.. at least that's what GCC says...

I myslef am not 100% sure what the f... is going on already. Let's step it up a notch (this is from Google Chrome source, at least that's what the place I found it in by googling says, I just googled for 'Chrome array size macro template' and similar, it's kind of 'common' trick that has few variants):


 template <typename T, size_t N>
char (& ArraySizeHelper (T (& array) [N])) [N];
#define arraysize(array) (sizeof (ArraySizeHelper (array))) 

It gets the size of array properly, protects against just passing in a pointer and is a compile constant too... Yes, the function will never be defined so it can't be really called.

Yes, this function takes and returns a reference to N sized arrays. Looks wonderful, doesn't it? If not, I think you can omit the name of input param:


template <typename T, size_t N>
char (& ArraySizeHelper (T (&) [N])) [N];
#define arraysize(array) (sizeof (ArraySizeHelper (array))) 

Much better now...

Back in the bad old days there were many practices that were followed but they worked. Things like intentionally illegally modifying your call stack so your function returned to a different location, building well-formed but abusive jump tables, and writing self-modifying code. Thankfully these practices are almost universally ended these days.

20 or 30 years ago things were different. In very rare cases these cute tricks were sometimes useful in performance-significant spots, and when your processor speed was two-digit megahertz, that could make a lot of sense. Rules were different in machines with no pipeline, between zero to 32 bytes of cache, and every new instruction causing a round trip to main memory. It took every trick you could imagine to make a game on a 12 MHz machine. I remember my first blissful project where we could assume a flat memory model, at least 33 MHz, and were guaranteed four megabytes of memory. We still didn't have hardware floating point, but the early 90s sure enabled programmer laziness. ;-)

When I graduated those neat tricks were commonplace. Usually they had several lines of comments or something like "This neat trick came from DDJ article in March 1993, so its okay", or other notices that helped others understand the craziness.

These days when I see weird syntax tricks like the ones above, I flag it for a code review. If they are particularly strange or repeat offenders I bring them up in our bi-weekly programmer team meetings to be shown to everyone on the projector. Code must be read and maintained by the entire team. If a junior programmer happens to be fixing a bug nearby, I don't want the tripping over some fancy language trick and introducing even worse bugs because of it.

In our code there are extremely rare situations where weird tricks are used, always accompanied with comments like: Profiling found the commented out algorithm to be an issue. This new fancy algorithm comes from the paper by Foo and Bar titled "Crazy fast algorithm for the thing", found at webpage dot com, a PDF should be in this folder. Please study the paper before touching this code.


Or for the particularly nasty code, just writing a big warning at the top: "This code is auto-generated. Do not modify it. Your changes will be discarded next time it is generated." That always keeps people out.


"This code is auto-generated. Do not modify it. Your changes will be discarded next time it is generated." That always keeps people out.
"Hey look! This dude made a hack generator, he must be a genius!"

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

Or for the particularly nasty code, just writing a big warning at the top: "This code is auto-generated. Do not modify it. Your changes will be discarded next time it is generated." That always keeps people out.

ROFL

This topic is closed to new replies.

Advertisement