Sign in to follow this  
Telastyn

C++ Trick

Recommended Posts

Telastyn    3777
Hey everyone, I'd just like to share a little trick I found with C++ that helped make the use of classes much nicer. [and partially for more experienced programmers to say that it's a terrible idea, heheh] The 'trick' is simply that class functions can be called from a null pointer. For example, say you have a class for a quake-style console. It has a simple interface of console::newtext(char *). Generally you'd like to use it to bump out error messages, game information, things like that. But how do you know if it's been setup already? You could make a singleton of it, but what about more complex things that might require multiple instances? You could assume a lot of things about game state, but what if something blows up the console, or if you can't make those assumptions? You could check that the global *gConsole exists each time you want to send info to it:
if (gConsole){
     gConsole->newtext("Monkeys.");
}
But that just gets a little repetative, and what if you or other users forget to check? Using the little trick, you can do something like this:

void    console::newtext(char *intext){
//
//
// 

if (!this){
     return;
}
// otherwise proceed as normal
}
Now gConsole::newtext will always "succeed", even if the actual display hasn't been setup yet, or if something unexpected has destroyed it. All of those if (gConsole) checks get moved into a reusable piece of code.

Share this post


Link to post
Share on other sites
coldacid    755
It is a terrible idea, because it'll just pretend that everything is alright. If it doesn't exist, for whatever reason, that's an error. At the least, an exception should be thrown, but unless it's something that can be caught and fixed during runtime, the program really should be allowed to bomb.

Share this post


Link to post
Share on other sites
uavfun    744
This will NOT work with virtual functions. In this case the function's address must be looked up in a pointer stored inside the object. So if you have a null pointer to the object, when it looks up the function in the table, it will end up calling who knows what - it's very unlikely it will manage to get to the if (!this).

Share this post


Link to post
Share on other sites
Telastyn    3777
But what if the object not existing is alright?

Say the game has 4-5 states, where the console exists in only 3 of them. Does every single area of code need to be aware of the game state? What if simply dumping the messages is the correct behavior?

Share this post


Link to post
Share on other sites
JohnBolton    1372
I have learned that it is useful to automatically substitute the words "unmaintainable" for "clever" and "bug generator" for "trick" when talking about code. The phrase "clever trick" becomes "unmaintainable bug generator".

Share this post


Link to post
Share on other sites
Washu    7829
Checking if it's null like that is silly. A better idea would be to assert the pointer. If the console is NULL at any point where you are trying to use it, chances are you've already skrewed up.

Share this post


Link to post
Share on other sites
Washu    7829
Well, null pointers usually signify a programming bug.

So, instead of saying:

if(Console)
Console->Write("Hello World");

you would write

assert(Console != NULL); before any usage of Console
...
Console->Write("Hello World");


This way an assert will be triggered before you ever get to use the Console, and instead of silently passing by the null console pointer, it will shout out to you (in debug mode) "Hey, you messed up! You have a null pointer here! Fix it biznitch!"

Share this post


Link to post
Share on other sites
Washu    7829
Yes, a very BAD design issue. If you're using a null pointer to indicate that something is unavailable, you need to redesign, badly.

Share this post


Link to post
Share on other sites
Telastyn    3777
Hrm. This might be a silly question, but to what?

Do others commonly have a little wrapper class that handles interface availability? Then the "avoid manager/handler classes" mantra around here seems to come into effect.

Do they effectively make the interface a prerequisite to the other classes? To me, missing a logging mechanism isn't a fatal error...

Do they attempt to create an unavailable interface each use [singletons]? That seems as though it would create a great amount of thrashing should the interface remain unavailable for some time.

Share this post


Link to post
Share on other sites
leiavoia    960
Just throwing out some ideas here:


static void SomeClass:UseMe( params ) {
// this is the "normal" way
if (i'm not available yet) { activate me; }

// might try...
if (i'm not available yet) {
load the required resource from another thread
or if it's a nice async deal and it doesn't hog
time while you wait, just:

put (params) onto some kind of processing queue

return; // (process things later when they get up and running)
}
}

Share this post


Link to post
Share on other sites
Telastyn    3777
Quote:
Original post by C-Junkie
You could take the quake route and make the console ALWAYS there...


In my current project it actually does almost always exist. Except for the first 2-3 lines anyways, and I could easily make it everything. I actually don't use this in my current console. I just assume that the console exists and never test the pointer.

Share this post


Link to post
Share on other sites
JohnBolton    1372
Quote:
Original post by Washu
Yes, a very BAD design issue. If you're using a null pointer to indicate that something is unavailable, you need to redesign, badly.


Wait a second Washu. That is how a Singleton works -- you reference a Singleton, and if its pointer is null, it means that it doesn't exist and so it creates one.

Anyway Telastyn, you are basically implementing a Singleton in a way that is hackish and difficult to maintain. Why not just do it right?

Share this post


Link to post
Share on other sites
Zahlman    1682
Urg. A static ("class") function is not supposed to require an instance for its operation. If you provide static console::newText, then people expect to be able to call console::newText as if it were just a free function. :\

Share this post


Link to post
Share on other sites
Telastyn    3777
Quote:
Original post by JohnBolton
Quote:
Original post by Washu
Yes, a very BAD design issue. If you're using a null pointer to indicate that something is unavailable, you need to redesign, badly.


Wait a second Washu. That is how a Singleton works -- you reference a Singleton, and if its pointer is null, it means that it doesn't exist and so it creates one.

Anyway Telastyn, you are basically implementing a Singleton in a way that is hackish and difficult to maintain. Why not just do it right?


I just rather recently realised that I could even do nullptr->stuff() without it causing things to explode.

I don't know, singletons to me seem to my admittedly limited perspective to be a 'clever trick' as you so describe it. They seem to be a slightly clumsy way for the programmer to ignore initialization procedure.

To me, I worry about the worst case. For singletons, this likely involves a situation like a file logger when the file cannot be created. Whenever a logging event occurs, it'll cause the entire app to thrash as it repreatedly tries to open the file for writing. For cases like that, I'd personally rather things fail more gracefully.

*sigh* unfortunately there's probably no better root reason than ignorance and stubbornness.

Share this post


Link to post
Share on other sites
CodeNow    100
Singletons do seem like a "trick" to me. They seem to be a trick to avoid having explicit global variables, yet still have that global variable availability. What am I missing?

Share this post


Link to post
Share on other sites
CJH    100
Quote:
Original post by Telastyn


void console::newtext(char *intext){
//
//
//

if (!this){
return;
}
// otherwise proceed as normal
}


You're throwing away a precious control path. Excpetions aren't ideal either; they add copius amounts of control paths.

Share this post


Link to post
Share on other sites
Hello,

(M-x mode-thread-hijack-on)
Quote:
Original post by CodeNow
Singletons do seem like a "trick" to me. They seem to be a trick to avoid having explicit global variables, yet still have that global variable availability. What am I missing?


You are missing the real meaning of what a singleton is: of course, this a global instance of an class. But it is the only one global instance of a particular class, while a classical global variable do not ensure unicity. Therefore, a singleton is not used to declare a global variable but to declare a variable that is unique and global to whole program. That is a rather important semantic difference (and good programming is also about semantics)
(M-x mode-thread-hijack-off)

Quote:
Original post by C-Junkie
You could take the quake route and make the console ALWAYS there...


I don't think this is a good idea. What if the console uses a lot of memory? Do you apply the same paradigm for an in-game editor?

Quote:
Original post by _the_phantom_
then you seriously need to rethink your design

To _the_phantom_ and Washu (seem to share a similar POV): I don't think having NULL pointers which hides a non-existant object is a sign of bad design. I don't see why having to deal with a NULL pointer should always throw either an assert or an exception.

If you speak about the design (if a feature is not used, then you must not have any sign of it in the code): this is very hard. Consider the case where you are using a debug log. Do you want to do:

CGame *game;
if (myExternalConfigFileSaysIMustLog) {
game = new CGameWithoutLog();
} else {
game = new CGameWithLog();
}
game->run();
delete game;

Or do you prefer

CLogger *logger;
if (myExternalConfigFileSaysIMustLog) {
logger = new CLogger();
} else {
logger = NULL;
}
CGame *game = new CGame(logger);
game->run();
delete game;

From a design point of view, I'd do the later because I don't want to write my CGame code twice (this is nonsense).

If you speak about the code construction (testing a NULL pointer instead of a false boolean): I think that having to maintain a pointer AND a boolean which explicitely says that this pointer is NULL or non-NULL IS a bad design - because it breaks unicity and is error prone. IT may leads to code like this:

if (hasThisFeature) {
assert(objectInstance != NULL);
objectInstance->doSomeStuff();
}

Which is rather weird (and fits well in the "more code, more bug" way of life).

@Telastyn: as uavfun pointed out, this code construction will not work with virtual function and therefore is very dangerous. VC++ (both 6 and .net) will generate this code when calling a virtual function:

mov eax, ecx[n]
call [eax]

ecx contains this. If this is NULL you call a very strange function. The correct thing to do is to test wether the object exists, call the function if it exists, and throw an assert if the function is called with this == NULL.

What you suggest is what I'd call (no intended offense) lazy programming. This is not a good programming practice. Good programming produces the right amount of code, not the minimal amount of code.

Regards,

Share this post


Link to post
Share on other sites
Shannon Barber    1681
Use static member functions if you want to do something like that; obj->method() says I have a valid object and am invoking some method on it. Class::method() is for the type of stuff you're doing.

Quote:

If you speak about the code construction (testing a NULL pointer instead of a false boolean): I think that having to maintain a pointer AND a boolean which explicitely says that this pointer is NULL or non-NULL IS a bad design - because it breaks unicity and is error prone. IT may leads to code like this:


Why are you calling methods on objects that don't exist? There should be a container that holds valid objects (or pointers to them) or there should be a deterministic order of operations.

The only case I think is acceptable is the use of a null-pointer check for an optional "return" parameter; if they pointer isn't null we copy some result into it, otherwise we ignore it, and that's just for simple structures.

...
Quote:
Original post by CodeNow
Singletons do seem like a "trick" to me. They seem to be a trick to avoid having explicit global variables, yet still have that global variable availability. What am I missing?

Real singletons exhibit polymorphic behavior (you decide once what object to create, then everyone uses that object from then on).

Share this post


Link to post
Share on other sites
Telastyn    3777
Ah, thank you for the explination as to why that is broken for the specific case of virtual functions [which I've not used until recently].

As for why, generally I've used the !this check in lists [to signify an empty list] or with optional values [say... the current equipped weapon, which might be nothing]. I'd just rather write that check once than forget it in various places.

Share this post


Link to post
Share on other sites
Zahlman    1682
For "the currently equipped weapon, which might be nothing", you may be interested in the Null Object pattern.

To MKH: I finally found an application where a polymorphic Singleton might actually be useful (because implementation actually has to be decided at runtime). But I already had code that assumed a static interface. My solution was to create an object of some subclass at runtime, and delegate the static "interface" methods to appropriate object "implementation" methods. Outside code is unaware of the subclasses - it's a Facade basically. I am pretty happy with the result - although it looks kind of unusual internally, the change is transparent to the rest of the code base.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this