Skip to main content
GameDev.net gamedev.net
🔒 Locked

Source engine code: .... uh, what?

Started by Endar Mar 12, 2007 at 7:30 PM 26 replies 2.7k views
Original Post
Endar
Endar
I'm having a little bit of trouble in figuring out just what a piece of code does. Or rather, what the point of it is. If anyone's looking a the Source engine code, this is on line 244 in 'public\tier1\convar.h', and it deals with returning a boolean value for a console variable.

FORCEINLINE bool GetBool() const { return !!GetInt(); }
I haven't been able to find the definition of 'GetInt', but I'm fairly certain that it wouldn't do anything strange. What is the point of the two '!!'? I mean, just say the value returned was 5. This evaluates to 'true' in C++, so then !5 is false, and !!5 is true again. Could it be that they didn't want a compiler warning and they just don't like using casts?
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
osmanb
osmanb
Yes, that's a not-uncommon trick for converting integral types into bool without an explicit cast.
Endar
Endar
Quote:
Original post by Anonymous Poster
This the Source engine CODE or the SDK code?

One is illegal to posses without a license, the other is fine.


SDK

Quote:
Original post by osmanb
Yes, that's a not-uncommon trick for converting integral types into bool without an explicit cast.


But, is there something that makes it better than an explicit cast? Or, they are pretty much equal in this situation, and the programmer just decided to do it this way?
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
iNsAn1tY
iNsAn1tY
Quote:
Original post by Endar
Quote:
Original post by osmanb
Yes, that's a not-uncommon trick for converting integral types into bool without an explicit cast.

But, is there something that makes it better than an explicit cast? Or, they are pretty much equal in this situation, and the programmer just decided to do it this way?

Well, syntactically, it's worse than an explicit cast. In fact, it's probably worse than a C-style (bool) cast, in that it's confusing and kinda unintuitive. One of the advantages of static_cast is that it's clear and concise, if a little verbose. I don't know if it's a performance thing. I'd guess there's no difference, and it's just down to personal choice.
My opinion is a recombination and regurgitation of the opinions of those around me. I bring nothing new to the table, and as such, can be safely ignored.[ Useful things - Firefox | GLee | Boost | DevIL ]
Alpha_ProgDes
Alpha_ProgDes
Quote:
Original post by Endar
Quote:
Original post by osmanb
Yes, that's a not-uncommon trick for converting integral types into bool without an explicit cast.


But, is there something that makes it better than an explicit cast? Or, they are pretty much equal in this situation, and the programmer just decided to do it this way?

i don't think you caught the joke [smile]

Beginner in Game Development?  Read here. And read here.  
SunTzu
SunTzu
Quote:
Original post by Endar
But, is there something that makes it better than an explicit cast? Or, they are pretty much equal in this situation, and the programmer just decided to do it this way?


Pretty much equal. Some people just like to do it that way. Same as some people do:

if ( ptr )

...and some people, wishing to be more explicit, do:

if ( ptr != NULL )

Or indeed, some might do:

if ( n == 1 )

...while others might do:

if ( 1 == n )

in the hope that if they accidentally type = instead of == they'll get a compile error instead of incorrect behaviour. (Seems silly to me. If you remember to have the values that way around, aren't you likely to remember to use == too? But there you have it).
tok_junior
tok_junior
!

NOT.

Not at all funny, but i think it was the aforementioned joke ;)
osmanb
osmanb
No joke intended. I was simply observing that I've seen this done several times, in various code-bases. I also originally wrote that it was "unfortunately common", but decided not to pass judgement on the technique. Ultimately, though, I think it's a fairly silly method of conversion, especially because you're basically mandating the method of conversion to the compiler, rather than just using a cast and letting the optimizer pick the best approach. (And, of course, this method is far less obvious to someone reading the code.)
Alpha_ProgDes
Alpha_ProgDes
Quote:
Original post by Anonymous Poster
Quote:
Original post by Alpha_ProgDes
Quote:
Original post by Endar
Quote:
Original post by osmanb
Yes, that's a not-uncommon trick for converting integral types into bool without an explicit cast.


But, is there something that makes it better than an explicit cast? Or, they are pretty much equal in this situation, and the programmer just decided to do it this way?

i don't think you caught the joke [smile]
I don't get it even though you highlighted it. Why would that be funny to anyone? And Endar's question is valid regardless of whether he got the "joke" or not.

I have no idea how word-play invalidates one's question. Furthermore I have no idea how you put the two together. Next time get a grip.

And for those of you who still don't get it.

5 (in boolean is true) -> !5 (in boolean is false) -> !!5 (in boolean is true).
Now...
common (means common)-> uncommon (obviously means the opposite of common) -> not-uncommon (means common)

That's all. No big deal. Carry on.
Beginner in Game Development?  Read here. And read here.  
Alpha_ProgDes
Alpha_ProgDes
Quote:
Original post by Anonymous Poster
If let's say, bool is 8 bits long, and myInteger value is 256, wouldn't (bool)myInteger be false where as !!myInteger is true?
Just asking.

is the CPU little-endian or big-endian?
Beginner in Game Development?  Read here. And read here.  
SunTzu
SunTzu
Quote:
Original post by Anonymous Poster
If let's say, bool is 8 bits long, and myInteger value is 256, wouldn't (bool)myInteger be false where as !!myInteger is true?
Just asking.


No, in the same way that (float)5 is 5.0f, not... well, whatever binary 00000101 is as a float. Casting does not mean "interpret my bits in a different way", it means "interpret my value in a different way".

Implicit conversion to bool treats zero as false and non-zero as true.

Edited because I'm stupid.
Excors
Excors
Quote:
Original post by SunTzu
well, whatever binary 00000101 is as a float.

4.60060298822481e-041, assuming it's little-endian, I think.
SunTzu
SunTzu
Yeah... I was just about to say that... (cough)...
Cowboy Coder
Cowboy Coder
With this code in Visual Studio 2005, Express edition:
class CTest{public:	inline int GetInt() const { return m_value; }	inline void SetInt(int v) {  m_value = v; }	inline bool GetBoolNotNot() const { return !!GetInt(); }	inline bool GetBoolCast() const { return (bool)GetInt(); }private:	int	m_value;};CTest X;bool b;int i;int _tmain(int argc, char* argv[]){        // Uncomment any one line below	//b = X.GetBoolNotNot();	//b = X.GetBoolCast();	//b = X.GetInt();	//i = X.GetBoolNotNot();	//i = X.GetBoolCast();	i = X.GetInt();	return 0;}


All three versions for b = ... compile to the exact same code:
  cmp         dword ptr [X],0   setne       al     mov         byte ptr ,al 


You don't even need a cast to bool if you are going to use it as a bool. I think, rather paradoxically, the only reason you might be wanting to cast to bool would be if you were going to use it as an integer.

Both
i = X.GetBoolNotNot();
i = X.GetBoolCast();
cleverly compile to:
  xor         eax,eax   cmp         dword ptr [X],eax   setne       al     mov         dword ptr ,eax 


Whereas
i = X.GetInt();
gives:

  mov         eax,dword ptr [X]   mov         dword ptr ,eax 


I think the use of !! dates from some earlier compiler, or simply whatever occurred to someone to solve the problem, or maybe just laziness in typing became habit then inherited code.
Cowboy Coder
Cowboy Coder
Can't you just write the appropriate cast operators?
Enigma
Enigma
Quote:
Original post by Anonymous Poster
There isn't really a better alternative in this case.

Sure there is. You could accept that a number is not a boolean and simply rewrite operator! as an is_zero member function instead:
const bool is_zero() const {	using namespace std;	return search_n(data.begin(), data.end(), data.size(), 0U) == data.begin();}const ubigint& operator *=(ubigint q) {	ubigint t;	t = *this;	*this = (ubigint)0U;	do {		if ((q.data[0] & 1U) != 0U)			*this += t;		q >>= 1;		t <<= 1;	} while (!q.is_zero()); // <- here	return *this;}
Σnigma
SunTzu
SunTzu
If you want your bigInt class to behave as class to an int as possible, you need to support implicit conversion to bool.
eq
eq
Haven't read the whole thread, so sorry if I'm missing something.
I wouldn't write the code like the OP's, but rather use:

FORCEINLINE bool GetBool() const { return GetInt() != 0; }

No casts at all (implicit or explicit) and like others mentioned it will likely compile to the exact sequency of machine code...
Cowboy Coder
Cowboy Coder
But why?

You could write it as:

FORCEINLINE bool GetBool() const { return GetInt()?1:0; }

Which also compiles to the exact same code, and perhaps conveys the intent of the function better. Or even:

FORCEINLINE bool GetBool() const {  if (GetInt() != 0)    return 1;  else    return 0;}


if you want to labor the point (again, this compiles to the same code in release mode).

The real question is why do you need a GetBool member function in the first place, since int->bool conversion is implicit. I suspect it's really about converting to 1 and 0, not true and false.
Sphet
Sphet
Quote:
Original post by Cowboy Coder
But why?

You could write it as:

FORCEINLINE bool GetBool() const { return GetInt()?1:0; }

Which also compiles to the exact same code, and perhaps conveys the intent of the function better. Or even:

*** Source Snippet Removed ***

if you want to labor the point (again, this compiles to the same code in release mode).

The real question is why do you need a GetBool member function in the first place, since int->bool conversion is implicit. I suspect it's really about converting to 1 and 0, not true and false.



From all my console compiler experiences I would guess this code was written by someone who has been bitten by an old version of GCC or metrowerks. These compilers will generate different code from different compares with temporaries being added and weird casts to long quad words on PS2 and garbage like that. If the code is called a lot I can understand making the change, but for something like this I'm not so sure. The source SDK is full of some pretty brutal code.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.