开发者

What is the downside of using the preprocessor to define a function call?

开发者 https://www.devze.com 2022-12-17 12:43 出处:网络
I would like to know what开发者_开发技巧 the cons are of using the preprocessor in such a way:

I would like to know what开发者_开发技巧 the cons are of using the preprocessor in such a way:

#define SOME_FUNCTION someFunction(someArgument)

Basically I feel like this is wrong (or certainly not a best practice) - but I'm not sure why... my preprocessor skills are rusty at best.


The problem is that the arguments are re=evaluated each time they are used:

#define MIN(A,B)   ((A) < (B))?(A):(B);

Notice that I have to wrap all the arguments in '(' ')' to make sure the expression evaluates corectly. But what happens if we do this?

int  s = MIN(++current,Max);

Coding this I would expect current to be incremented once before the function is called. But because it is a macro it is incremented once in the test and a second time if it is still smaller than Max


A downside? Usually a macro definition doesn't end up in the executable's symbol table. Slightly more difficult to debug.


There are several problems you can think of:

  • In C++ the macro has no namespace and class scope, so it is the same everywhere. An example for this are the unfortunate min and max defines somewhere in windows.h. If you are programming for windows and include windows.h and want to write std::numeric_limits::max() then max will be replaced by some code... This leaves uncompilable code after preprocessor run. (Okay there are ways to turn off min/max macros in windows.h but it's still poor design!)
  • The macro can't be debugged nicely. The debugger will stop on the line the macro is used not on the code inside the macro...
  • Possible reevaluation of the macro parameters (you could prevent this by having a block with local variables inside the macro but this would make debugging even worse!)


Well if you must do that (and there are some occasions when you might), then you should at least define the macro as "function-like" thus:

#define SOME_FUNCTION() someFunction(defaultArgument)

otherwise you would write code that looked like an assignment by a constant when it was in fact a function call; i.e;

x = SOME_FUNCTION ;  // hidden function call

but with a "function-like" macro you would be obliged to write:

x = SOME_FUNCTION() ;  // shorthand function-call with default argument

Which better matches the pre-processor syntax with the language syntax.

Generally function-like macros are best avoided, but some are more insidious that others, this is by no means the worst case. However, you might just as easily write a function wrapper in C, or in C++ use a default argument, and this would be preferable in most cases.


This is occasionally useful in a very closed domain for defining process steps in a more readable fashion:

void myfunc() {
  DO_STEP_ONE;
  THEN_ANOTHER_STEP;
  KEEP_GOING;
  LAST_STEP;
}

But usually it just makes code harder to read and understand.

Unless its worth the readers of your code getting to know what those #define's mean in detail, and it's some kind of short cut, you're just getting people to look in two places to understand a line of code (top of file and in function) rather than one.

I very rarely use this kind of approach.


That will depend on the language, compiler, etc, etc, etc...

However, there's nothing wrong with it, once as the name implies those directives occurs PREviously the PROCESS of compiling.

The pre-processor removes all constant references by its real value, all the pseudo-function with the proper code, and so on...


All problems in computer science can be solved by another level of indirection

This is one of these extra indirection levels :-)

Say that at some point that function needs another argument or you don't need to call it at all, you can change the #define once instead of changing all the places where the function is being called.

Useful for developing, but dangerous to keep in production code... I'd run the preprocessor to replace that rule once I have a mature code and know that I won't need to change it.


Why would you look for disadvantages in using specific preprocessor code? Using preprocessor macros for anything other than conditional compilation (including include guards) should be automatically considered bad. The correct question to ask is whether there are advantages to a particular use.

Preprocessor macros don't respect scope or usage in any way. They can screw up perfectly good code in unexpected and difficult-to-find ways. Always avoid them unless you've got a good reason to use one.


The downside is that you are hiding the code. The upside is that you are hiding the code.

The downside usually outweights the upside.

Usually this particular approach is pretty much useless and unless the call looks more like

someModule->someStorage->functionList[storage.getFunctionName].pointer->SomeFunction(...equally obscure argument...);

there is no point doing so. If only the argument is an obscure call, shorthand only the argument. If it's only the function, shorthand only the function. If both, you might be better off with

 SOME_FUNCTION(SOME_ARGUMENT);

If the function is never called with anything else, you might consider removing it from argument list and obtaining inside the function body. And if the pair repeats very often, in small variations, you might consider wrapper functions.

After you make several bugs in the code of a macro, you will learn it's pain to debug them and you won't use them frivolously.

0

精彩评论

暂无评论...
验证码 换一张
取 消