Removing code from Release builds

Started by
11 comments, last by MaulingMonkey 18 years, 7 months ago
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!
Advertisement
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.
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;
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!
class Logger
{
Logger() {}
~Logger() {}

#ifdef _DEBUG
template Logger &operator <
class Logger{ Logger() {} ~Logger() {}#ifdef _DEBUG template <class T> Logger &operator << (const T &val) {  //Logging stuff  return *this; }#else template <class T> Logger &operator << (const T &val) {  return *this; }#endif};


Can someone please kill my bad post above? Thanks.
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
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!
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;

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;

This topic is closed to new replies.

Advertisement