Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
429 views
in Technique[技术] by (71.8m points)

compiler warnings - Will a "variableName;" C++ statement be a no-op at all times?

In C++ sometimes a variable will be defined, but not used. Here's an example - a function for use with COM_INTERFACE_ENTRY_FUNC_BLIND ATL macro:

HRESULT WINAPI blindQuery( void* /*currentObject*/, REFIID iid, void** ppv, DWORD_PTR /*param*/ ) 
{
    DEBUG_LOG( __FUNCTION__ ); //DEBUG_LOG macro expands to an empty string in non-debug
    DEBUG_LOG( iid );
    iid; // <<<<<<<----silence compiler warning
    if( ppv == 0 ) {
        return E_POINTER;
    }
    *ppv = 0;
    return E_NOINTERFACE;
}

In the above example iid parameter is used with DEBUG_LOG macro that expands into an empty string in non-debug configurations. So commenting out or removing the iid variable name in the signature is not an option. When non-debug configurations are being compiled the compiler spawns a C4100: 'iid' : unreferenced formal parameter warning, so in order to silence the warning the iid; statement that is believed to be a no-op is added.

The question is the following: if we have any of the following declarations:

 CSomeType variableName; //or
 CSomeType& variableName; //or
 CSomeType* variableName;

will the following statement in C++ code:

variableName;

be a no-op at all times independent of what CSomeType is?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Yes, but you'll likely get another warning.

The standard way of doing this is: (void)iid;.


Very technically, this could still load iid into a register and do nothing. Granted that's extremely stupid on the compilers part (I doubt any would ever do that, if it does delete the compiler), but it's a more serious issue if the expression to be ignored is something concerning observable behavior, like calls to IO functions or the reading and writing of volatile variables.

This brings up an interesting question: Can we take an expression and completely ignore it?

That is, what we have now is this:

#define USE(x) (void)(x)

// use iid in an expression to get rid of warning, but have no observable effect
USE(iid); 

// hm, result of expression is gone but expression is still evaluated
USE(std::cout << "hmmm" << std::endl);

This is close to a solution:

// sizeof doesn't evaluate the expression
#define USE(x) (void)(sizeof(x))

But fails with:

void foo();

// oops, cannot take sizeof void
USE(foo());

The solution is to simply:

// use expression as sub-expression,
// then make type of full expression int, discard result
#define USE(x) (void)(sizeof((x), 0))

Which guarantees no operation.

Edit: The above indeed guaranteed no effect, but I posted without testing. Upon testing, it generates a warning again, at least in MSVC 2010, because the value isn't used. That's no good, time for more tricks!


Reminder: We want to "use" an expression without evaluating it. How can this be done? Like this:

#define USE(x) ((void)(true ? 0 : (x)))

This has a simple problem like last time (worse actually), in that (x) needs to be be convertible to int. This is, again, trivial to fix:

#define USE(x) ((void)(true ? 0 : ((x), 0)))

And we're back to same kind of effect we had last time (none), but this time x is "used" so we don't get any warnings. Done, right?

There is actually still one problem with this solution (and was present in the last un-solution as well, but went unnoticed), and it comes up in this example:

struct foo {};
void operator,(const foo&, int) {}

foo f;
USE(f); // oops, void isn't convertible to int!

That is, if the type of the expression (x) overloads the comma operator to something not convertible to int, the solution fails. Sure, unlikely, but for the sake of going completely overboard, we can fix it with:

#define USE(x) ((void)(true ? 0 : ((x), void(), 0)))

To make sure we really end up with zero. This trick brought to you by Johannes.


Also as noted, if the above wasn't enough, a stupid enough compiler could potentially "load" the expression 0 (into a register or something), then disregard it.

I think it's impossible to be rid of that, since we ultimately need an expression to result in a type of some sort to ignore, but if I ever think of it I'll add it.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...