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

Removing code from Release builds

Started by SirLuthor Sep 2, 2005 at 5:41 PM 11 replies 1.9k views
Original Post
SirLuthor
SirLuthor
Hell once again! I bring my queries, to lay at your feet, in hopes of answers to apply [smile] Anyway, here's the deal. I've got a console class, which opens up a console, and defined a number of writing operators (<< at the moment) for different types. So a typical line logged to the console would look about like logging a line to cout, or an fstream, or whatever. Now, my problem lies in wishing to remove this code entirely in release builds, that is, have the preprocessor remove it if possible, otherwise, have no overhead in the runtime code. Now, is there any way you people know to do this? I've tried a number of things with the preprocessor, such as __noop, etc, but it's not doing anything good for me, and at the moment, I have a terrible hack, which is surrounding the operators and the arguments all in one macro. It looks bad, it doesn't fit in with C++ syntax, etc. Now, again, is there a better way to do this? Would you personally find it usable to use a function for writing to the console, instead of the average string of ' << "someString" << someInt << "SomeOtherString!" << endl;' Iðm personally fine with that method, but when I distribute this, I don't want to alienate lots of people with that feature. Plus, if I do things that way, you can't define your own writing operators. Anyway, any thoughts on how to improve this problem would be much appreciated. Thanks! Also, just for the record, if there is an inlined function call, with no body at all to the function ie. no code, just a return value) is there any overhead when calling it?
Free speech for the living, dead men tell no tales,Your laughing finger will never point again...Omerta!Sing for me now!
XXX_Andrew_XXX
XXX_Andrew_XXX
Have a look at John Torjo's logging library he uses a trick with variables declared in an if statement having scope over the whole statement.

However even this solution has a price (albeit quite small), which from memory is something like one memory allocation an if statement and a few pointer copies and dereferences.

I don't believe a better solution is possible without having the whole statement within a macro (which is what you've got) so it comes down to a tradeoff between ease of use and performance.

/* Simple Case (just log default information) */LOG( logger ); /* OR *//* Message case (default + extras) */LOG( logger ) << "foo" << "bar" << "blah" << endl;  


The log macro with the stream operators appended expands to:

if( SomeStreamClass stream = logger.getTemporaryStream() )    stream;/* OR */if( SomeStreamClass stream = logger.getTemporaryStream() )    stream << "foo" << "bar" << "blah" << endl;


The reason that the assignment within the if is valid is that SomeStreamClass has a conversion to bool. The destructor of SomeStreamClass also writes to the logs, and will be triggered when the if statement ends (after all of the stream operations have executed). Note that the destructor of SomeStreamClass should have a 'try{ } catch(...) {}' block to ensure that it is non-throwing.
MaulingMonkey
MaulingMonkey
Quote:
Original post by SirLuthor
Also, just for the record, if there is an inlined function call, with no body at all to the function ie. no code, just a return value) is there any overhead when calling it?


Compiler dependant, but I would throw out any compiler where this is the case. I can imagine C compilers from 1985 doing this, that's about it.

The cleanest way for random interspersed debugging code is probably this:

#ifdef DEBUG    const bool debug = true;#else //def DEBUG    const bool debug = false;#endif //ndef DEBUG...int main () {    if ( debug ) clog << "Debug: I like pie" << endl;}


Modern compilers are smart enough that they'll go "Oh, debug is a (nonvolatile) constant. That means I don't really need to have this be a conditional. Hmm - this is allways [true/false]. That means I [do/do not] need to include the body.

And yet, it still preforms syntax checking :-).

You can also do finer grained debugging - e.g.:

const bool debug_gadget_13 = true;
const bool debug_gadget_14 = false;

if ( debug_gadget_13 ) clog << "Debug: Oh noes, gadget 13 is FUBAR again?" << endl;
SirLuthor
SirLuthor
Alright, thanks, that helps, but I do have another question along the lines of using those constant variables and such, namely, is there any way to test against on of those variables in an #if statement? I've been messing around with that, and using const ints instead of #defines for control variables, but I'm a bit stuck here. Here's what I'm trying to do.. Basically, when a certain value was defined (this was before I started moving to variables instead) an extern global console object is created. However, if it is not defined, it should not be created, but to have that occur, I need to be able to physically remove the code from the file. Now, the preprocessor is of course clearly made for just this sort of thing, but I can't get it to evaluate constant variables.

So I'm in a fix. And yes, I know there are other ways I can do the whole console-on/console-not-on thing, but eventually, this problem will come up again without a doubt, although in another form. So I'd really appreciate it if someone could point out, if it's possible at all, to do what I'm talking about. Thanks!
Free speech for the living, dead men tell no tales,Your laughing finger will never point again...Omerta!Sing for me now!
Conner McCloud
Conner McCloud
Code dupliation sucks:
class Logger{   Logger() {}   ~Logger() {}   template <class T> Logger &operator << (const T &val)   {   #ifdef _DEBUG       //Logging stuff   #endif   return *this;   }};


Boost has a preprocessor library that you may look into. I've never looked into it, but glancing through other boost header files it seems to offer some interesting functionality. Although I can say that the preprocessor almost certainly can't consider variables except possibly using a compiler specific extension...it knows nothing about the underlying programming language, it just messes with raw text files.

CM
SirLuthor
SirLuthor
Thanks Connor, that was what I had thought (was afraid of). Oh well, looks like I'll have to modify my two-minute design again [grin]
Free speech for the living, dead men tell no tales,Your laughing finger will never point again...Omerta!Sing for me now!
MaulingMonkey
MaulingMonkey
Quote:
Original post by SirLuthor
Alright, thanks, that helps, but I do have another question along the lines of using those constant variables and such, namely, is there any way to test against on of those variables in an #if statement? I've been messing around with that, and using const ints instead of #defines for control variables, but I'm a bit stuck here. Here's what I'm trying to do.. Basically, when a certain value was defined (this was before I started moving to variables instead) an extern global console object is created. However, if it is not defined, it should not be created, but to have that occur, I need to be able to physically remove the code from the file. Now, the preprocessor is of course clearly made for just this sort of thing, but I can't get it to evaluate constant variables.

So I'm in a fix. And yes, I know there are other ways I can do the whole console-on/console-not-on thing, but eventually, this problem will come up again without a doubt, although in another form. So I'd really appreciate it if someone could point out, if it's possible at all, to do what I'm talking about. Thanks!


You could use template specialization.

template < bool debug > class logger;template <>class logger< true > {    std::ostream & os;public:    void log( const std::string & data ) const {        os << data << endl;    }};template <>class logger< false > {public:    //no log decleration if we want a compile error when attempted to be used    void log( const std::string & data ) const; //if we want a linker error    void log( const std::string & data ) const {        //noop: if we want it just not to happen    }};logger< debug > my_debug_logger;

rick_appleton
rick_appleton
I'm using something I got from GPG I think.

It relies on shortcuts when evaluating something. The idea is that when you have
false && DoFunc();

DoFunc is never called. So in your case you'd have something like:
#define DEBUG_COUT DEBUG && coutDEBUG_COUT << "output" << endl;
SirLuthor
SirLuthor
Heh, Monkey, as always you've got the good advice (which I continually appreciate, by the way, wherever it occurs) that I had planned on doing my self, however, I had to scrap that idae, because MSVC6 freaks out with most if not all sorts of template specializations.

Rick: Ya, I'm doing something like that with the debug output statements now, however, it still doesn't solve the problem of creation of the logger, that is, it being created whether it should be or not. I think I'm just going to have to use an 'activate' function which I'll plug into my initializer function list or something.

Anyway, thanks folks, as always, the help is well and truly appreciated!
Free speech for the living, dead men tell no tales,Your laughing finger will never point again...Omerta!Sing for me now!
Conner McCloud
Conner McCloud
Quote:
Original post by MaulingMonkey
You could use template specialization.

template < bool debug > class logger;template <>class logger< true > {    std::ostream & os;public:    void log( const std::string & data ) const {        os << data << endl;    }};template <>class logger< false > {public:    //no log decleration if we want a compile error when attempted to be used    void log( const std::string & data ) const; //if we want a linker error    void log( const std::string & data ) const {        //noop: if we want it just not to happen    }};logger< debug > my_debug_logger;

Hmm...I like the looks of that. Seems like you could do some much fancier stuff than with straight preprocessor trickery. Thanks for the tip [grin] I hate templates with a passion, but they're so damn useful sometimes...

CM
MaulingMonkey
MaulingMonkey
Quote:
Original post by SirLuthor
Heh, Monkey, as always you've got the good advice (which I continually appreciate, by the way, wherever it occurs)

Anytime :-).
Quote:
that I had planned on doing my self, however, I had to scrap that idae, because MSVC6 freaks out with most if not all sorts of template specializations.

Oh, yes. My apologies and sympathies, you poor VC6 user. Prehaps another poster (which has the link which I do not) can tempt you with the option of upgrading to the MS V2K5 beta compiler for free (whislt using the VC6 IDE)? Or have they closed the open beta allready?

Quote:
Original post by Conner McCloud
Hmm...I like the looks of that. Seems like you could do some much fancier stuff than with straight preprocessor trickery. Thanks for the tip I hate templates with a passion, but they're so damn useful sometimes...


You poor templates-hater, you have my sympathies as well.

Topic Locked

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

Sign in to reply to this topic.