Welcome, Guest. Please login or register.

Author Topic: Dear God, WHY?!?!  (Read 7500 times)

Description:

0 Members and 1 Guest are viewing this topic.

Offline ejstans

  • Newbie
  • *
  • Join Date: Jun 2009
  • Posts: 48
    • Show only replies by ejstans
Re: Dear God, WHY?!?!
« Reply #14 from previous page: August 08, 2009, 10:52:22 PM »
Quote from: Karlos;518506
Tried that, no joy.

After some experimentation I have discovered that pretty much any C/C++ operator (especially ., -> etc) doesn't like being used in ## concatenation. Which is completely stupid.

IMO, that's totally and utterly broken. It should not be up the the preprocessor to dictate what constitutes "valid" output from concatenating two pieces of text. It should just do the job and if the result is meaningless garbage, that's up to the compiler to diagnose.

It isn't the first issue I have had with gcc 4's preprocessor. It has issues with computed includes too.

You are in undefined behavior-land. From the C++98 standard:

"16.3.3 The ## operator
 [...]
 3 For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace, each instance of a ## preprocessing token in the replacement list (not from an argument) is deleted and the preceding preprocessing token is concatenated with the following preprocessor token. If the result is not a valid preprocessing token, the behavior is undefined. The resulting token is available for further macro replacement. The order of evaluation of ## operators is unspecified."

The important part here is "If the result is not a valid preprocessing token, the behavior is undefined". Gcc is rather nice to point this out :)

Let's look at your fix:

#define PP_CONCAT(a, b, c) a##c::c##b

This (seemingly) works because 'a' and 'c' are concatenated (and 'c' and 'b' likewise) and since you pass in an empty argument the result is simply 'a' (and 'b') which is a valid preprocessing token. Or is it? Actually, passing in an empty argument results in undefined behaviour, so this is not a good fix...

But what if we suppose passing an in empty argument was in fact well-defined and acted like above? The result would simply be the same as:

#define PP_CONCAT(a, b) a::b

So why mix in the concatenting operator? :)
"It is preferable not to travel with a dead machine."

A500 1.3 / 512KiB slowmem / GVP HD8 w/ 8MiB fastmem & 52MB HDD
A600 2.05 / 1GB SSD
A1200 3.0 / Blizzard 1200/4 w/ 68882 @ 33MHz / 1GB SSD
A1200T 3.0 / Apollo 1260 w/ 68EC060 @ 50MHz & 16 MiB fastmem / 4GB SSD
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show only replies by Karlos
Re: Dear God, WHY?!?!
« Reply #15 on: August 08, 2009, 11:04:21 PM »
Quote from: ejstans;518537
But what if we suppose passing an in empty argument was in fact well-defined and acted like above? The result would simply be the same as:

#define PP_CONCAT(a, b) a::b

So why mix in the concatenting operator? :)

Well, yes, why indeed? It's a synthetic example to highlight the problem.

In actual fact, what I have is two macros, only one of which may be enabled, depending on the options passed in to make.

The first form of the macro does this:

#define THROW_NESTED_EXCEPTION(cname,except) EXNGPrivate::throw##cname##except

So, in the following code:

Code: [Select]
class Thread {
  public:
    class SecurityViolationError : public RuntimeError {};

   // etc...

  protected:
    void somethingPrivileged(); // may only be called by the current Thread

  private:
    static Thread* threadForTask(Task*);
};

//....

void Thread::somethingPrivileged()
{
  if (this!=threadForTask(::FindTask(0)) {
    THROW_NESTED_EXCEPTION(Thread, SecurityViolationError());
  }
  // do stuff
}


the following macro:

THROW_NESTED_EXCEPTION(Thread, SecurityViolationError());

becomes:

EXNGPrivate::throwThreadSecurityViolationError();

This function might be defined like so
Code: [Select]
void EXNGPrivate::throwThreadSecurityViolationError()
{
   // do various logging and other debug specific work

  throw Thread::SecurityViolationError();
}

In the alternative build option, the above function is never compiled and instead, the macro expands to:

throw Thread::SecurityViolationError();

Is that any clearer?
« Last Edit: August 08, 2009, 11:10:53 PM by Karlos »
int p; // A
 

Offline ejstans

  • Newbie
  • *
  • Join Date: Jun 2009
  • Posts: 48
    • Show only replies by ejstans
Re: Dear God, WHY?!?!
« Reply #16 on: August 08, 2009, 11:21:05 PM »
Actually, not clearer...

Is the second macro the following macro:

#define THROW_NESTED_EXCEPTION(cname, except) throw cname##::##except

If so, the argument from the synthetic example applies here as well: Why the ##-operator? No need for it. Try:

#define THROW_NESTED_EXCEPTION(cname, except) throw cname::except
"It is preferable not to travel with a dead machine."

A500 1.3 / 512KiB slowmem / GVP HD8 w/ 8MiB fastmem & 52MB HDD
A600 2.05 / 1GB SSD
A1200 3.0 / Blizzard 1200/4 w/ 68882 @ 33MHz / 1GB SSD
A1200T 3.0 / Apollo 1260 w/ 68EC060 @ 50MHz & 16 MiB fastmem / 4GB SSD
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show only replies by Karlos
Re: Dear God, WHY?!?!
« Reply #17 on: August 08, 2009, 11:26:34 PM »
Hmmm, actually...

This is starting to look like a redundant artefact from copying the first form of the macro...
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show only replies by Karlos
Re: Dear God, WHY?!?!
« Reply #18 on: August 08, 2009, 11:32:53 PM »
ROFLMAO

Ok, that works (of course). I guess sometimes a second pair of eyes and a functioning brain is useful :)

I'm actually struggling to remember why I used this form. No doubt it will come back and bite me later. At some point I was maintaining 3 different versions of the code (four if you count x86 and x64 linux targets as different).

Ironically the thread title is even more apt now. Why can't I see the wood for all these bloody trees?!
« Last Edit: August 09, 2009, 12:11:03 AM by Karlos »
int p; // A
 

Offline ChaosLord

  • Hero Member
  • *****
  • Join Date: Nov 2003
  • Posts: 2608
    • Show only replies by ChaosLord
    • http://totalchaoseng.dbv.pl/news.php
Re: Dear God, WHY?!?!
« Reply #19 on: August 09, 2009, 12:31:31 AM »
@Karlos

I looked at your code earlier for a while... and all I got was confused...
I just couldn't figure out what the ## was for.  I just assumed it was something to do with c++(which I don't know).   I have never found any use for ##.


@ejstans

Thanx for straightening him out. :)  You saved me a ton of typing.
Wanna try a wonderfull strategy game with lots of handdrawn anims,
Magic Spells and Monsters, Incredible playability and lastability,
English speech, etc. Total Chaos AGA
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16878
  • Country: gb
  • Thanked: 5 times
    • Show only replies by Karlos
Re: Dear God, WHY?!?!
« Reply #20 on: August 09, 2009, 11:13:55 AM »
You know, it's one of those stupid things where it takes someone else to point out what is obviously wrong.

The code was written a long time ago and always compiled without warnings under 2.95. It's only this weekend that I've ran into the problem whilst using 4 and only when compiling without the extended debugging.

Basically that set me off barking up the wrong tree :D

Quote
I looked at your code earlier for a while... and all I got was confused...
I just couldn't figure out what the ## was for. I just assumed it was something to do with c++(which I don't know). I have never found any use for ##.

Basically it's for concatenation in the preprocessor. I wanted to take the name of an exception and the name of the namespace or class it was nested in and turn it into either a function name or a normal exception throw depending on the compiler options. Turning it into a function name was a valid use of ##:

#DEFINE THROW_NESTED_EXCEPTION(cname, except) EXNGPrivate::throw##cname##except

when invoked thusly:

THROW_NESTED_EXCEPTION(Thread, SecurityViolationError());

generates:

EXNGPrivate::throwThreadSecurityViolationError();

That function in turn throws a Thread::SecurityViolationError after doing some other work.

Unfortunately, I got so blinded by the ## concatenation I didn't actually see what was wrong (or rather redundant) with the second form:

#DEFINE THROW_NESTED_EXCEPTION(cname, except) throw cname##::##except

which is supposed to produce

throw Thread::SecurityViolationError();

The fact is that 2.95.x was happy enough to concatenate "cname" with "::" and "::" with "except". On the face of it, regarding the preprocessor as a pure text processor, it still seems unreasonable that this gives a problem. However, it is expecting a valid preprocessor token to be generated by the ##, not just arbitrary text.

As ejstans observed, the use of ## in the second form was completely redundant anyway. Which is why I'm now wearing a conical paper hat with a big letter D on it ;)
« Last Edit: August 09, 2009, 11:23:26 AM by Karlos »
int p; // A