Sign in to follow this  
Jiia

Removing functions from builds

Recommended Posts

This is a pretty simple question, but I've never actually seen it discussed before. Is there a nice way to remove a function-call completely from the release build of your application without using #ifdef type branches all over? Here's my simple, brute force method of doing this. This is a task-logging routine that much of my engine uses to track progression of tasks:
#ifdef _DEBUG
	VOID ScLog(Val_SystemLog type,const CHAR *Format,...);
#else
	inline VOID ScLog(Val_SystemLog type,const CHAR *Format,...) {}
#endif
Then of course, I have to #ifdef _DEBUG the actual implementation in the cpp module. I'm not even sure this will remove the function-call overhead, but I can't imagine why it wouldn't. Guess I could look at the ASM result. Anyway, are there better ways to accomplish this? If it seems like I'm asking a dumb question, I probably am. Please feel free to provide the dumb answer [wink]

Share this post


Link to post
Share on other sites
Quote:
Original post by Andrew Russell
You know that no functions are inlined in debug builds anyway... Right?


He was inlining it when _DEBUG was not defined, not the other way around.

To the OP: I highly doubt there is a way other than using the preprocesser. That's what it's for, after all. You see this sort of thing in stardard libraries:

#ifdef _DEBUG
#define ASSERT(TEST) if(!(TEST)) { MessageBox(0, #TEST, "Assertion Failure", MB_OK | MB_ICONERROR); } else (void)0
#else
#define ASSERT(TEST)
#endif

Of course, empty macros like this will only work if either a)you're using a macro or b)the function it's replacing doesn't have any overloads or default values. If it has either, your method is better.
Quote:
I'm not even sure this will remove the function-call overhead, but I can't imagine why it wouldn't.

If your compiler doesn't remove the overhead when you're optimizing, you MUST get a new compiler.

Share this post


Link to post
Share on other sites
Quote:
Original post by ErzengeldeslichtesHe was inlining in when _DEBUG was not defined, not the other way around.


Exactly - he was pointing out that it was redundant.

Share this post


Link to post
Share on other sites
You could just #define ScLog(...) to remove the function call without the need for #ifdef. Or #define ScLog(Type, Format, ...) a macro to override the function call with "inline" code. In both of these options, the function still remains in the resulting object code, although its never called. This works because the preprocessor nabs the function call before the compiler tries to parse it. If you want to completely remove the function from existance, you'll have to wrap the function body in #ifdef ScLog/#endif. Otherwise, make sure you don't #define ScLog for the unit that contains the function body, otherwise you'll end up with something like this:

VOID ScLog(Val_SystemLog type,const CHAR *Format,...) {
// code goes here
}

...becomes...

VOID {
// code goes here
}

[Edited by - dcosborn on July 9, 2005 8:30:43 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Nitage
Quote:
Original post by ErzengeldeslichtesHe was inlining in when _DEBUG was not defined, not the other way around.


Exactly - he was pointing out that it was redundant.


No, he said "no functions are inlined in debug builds". How is this pointing out redundancy? In the debug build, the OP is not inlining, the function actually provides code. However, in the release build (when inling is useful) he inlines an empty function (which the compiler would inline anyway, but on that note, ALL inline declarations are "redundant" (more irrelevant)--the compiler will do what it darn well pleases.)

Share this post


Link to post
Share on other sites
I don't know if you guys noticed, but when _DEBUG is not defined the function has an empty body (I think that's how it's called) there, so I guess that what the OP wants is that when in release mode, the function won't be called.

@OP: If you're using Microsoft Visual Studio compiler you should be able to do this:

#ifdef _DEBUG
VOID ScLog(Val_SystemLog type,const CHAR *Format,...);
#else
# define ScLog __noop
#endif


Click here for more info on the __noop intrinsic.

Share this post


Link to post
Share on other sites
Quote:
Original post by Erzengeldeslichtes
Quote:
Original post by Nitage
Quote:
Original post by ErzengeldeslichtesHe was inlining in when _DEBUG was not defined, not the other way around.


Exactly - he was pointing out that it was redundant.


No, he said "no functions are inlined in debug builds". How is this pointing out redundancy? In the debug build, the OP is not inlining, the function actually provides code. However, in the release build (when inling is useful) he inlines an empty function (which the compiler would inline anyway, but on that note, ALL inline declarations are "redundant" (more irrelevant)--the compiler will do what it darn well pleases.)


Actually, I was working off the assumption that Jiia had simply removed the code from the {} to make his post shorter. So, given my assumption, I was correct [wink].


If there is no code to be run, his existing solution or Kamikaze15's or your own (Erzengeldeslichtes, for those people playing at home) is just fine. In theory, they should end up with same code (or lack thereof). This might not work if, say, you take the address of ScLog.

Jiia's original solution is nice to namespace as a plus. The other's have the advantage of giving 100% assurance that there will be no code generated.

Kamikaze15's has the negitive of being Microsoft-specific. Although the success of Jiia's original solution will also depend on the compiler.

Share this post


Link to post
Share on other sites
Wow, it looks like Microsoft built their __noop routine just for my situation. For readability sake, though, I would like it to be obvious as to what is happening. And it seems from the confusion of my post, my solution isn't very clear either.

As for the #define ScLog(...), will this actually work? I wasn't aware that macros could accept a variable argument list? A macro was my first guess to this problem, and that was what prevented me from using it in this function's situation.

Lastly, regarding the inline keyword. I don't see how it's use is redundant at all. How else could it be done? If I define the function body without the keyword, empty or not, in the header file, the compiler will have a fit. The resulting code would be included in every module that contains it. Is this incorrect?

And thanks for the advice.

edit:
Just to prevent further confusion, my goal really is to remove the function body. So what you see in my original post is the actual code. Including the empty {} and the ,... argument format. Those are not lazy posting placeholders :)

Share this post


Link to post
Share on other sites
I just tested your inline solution on my compiler (MSVC++ 7.1) on release mode and ran the disassembler and the there was no code related to the function at all, it even jumped a breakpoint I've set there, so you should be fine using inline.

Share this post


Link to post
Share on other sites
One trick that provides the most compatibility across compilers is to do something like the following:

#ifdef _DEBUG
#define FUNC(x) printf x
#else
#define FUNC(x)
#endif

void somefunction(char *ptr1, int val)
{
FUNC(("nothing to see here. %s - %d\n", ptr1, val));
}


This comes in handy if you need to pass a variable number of arguments but need the code removed for release builds. You can also do something like the following to reduce the number of macros needed for calls that are only used once or twice:

#ifdef _DEBUG
#define FUNC(x, y) x y
#else
#define FUNC(x, y)
#endif

void somefunction(char *ptr1, int val)
{
FUNC(printf, ("nothing to see here. %s - %d\n", ptr1, val));
}

Share this post


Link to post
Share on other sites
I'm wondering if the compiler will be okay handling more complex situations. Such as..

ScLog( SCLOG_WARNING, "%s %d %f", StringFunc(), rand() % 5, Time * 200.0f );

I can test it out. I'm just being lazy and can't remember where to find that ASM output option. I realize that some types of code in the parameter list would (or should) be included. But I wonder if that would actually be pulled out of the function argument list and called normally? It's no big deal, I'm just curious. I wouldn't include vital code in there anyways.

edit @ Helter Skelter: That's pretty clever. Another macro trick I was unaware of.

Share this post


Link to post
Share on other sites
Quote:
Original post by Jiia
edit @ Helter Skelter: That's pretty clever. Another macro trick I was unaware of.


Yeah it's pretty neat. Been using it for about a decade and I've only run into one compiler/preprocessor that didn't like it.

Also keep in mind that whether you use a FUNC like macro or the __noop directive statements like the following need to be avoided:

funcmacro(a++);


In release mode "a++" never happens. Now you would expect that the use of __noop would warn you about this but it doesn't.

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