4

Is it a good idea to implement business logic functions as macros?

I inherited some legacy C++ code and I find a whole lot of business logic functions are implemented as long cryptic macros.

Is there an advantage of macros over functions? What is the general rationale behind the use of macros?

What kind of logic is best suited for macros?

Here is a simple sample from code

#define INSERT_VALUES(IN,ID,EO)     {\
    double evaluationOutput = EO;\
    int controls = 0;\
    int input_controls = m_input_controls[IN];\
    if(m_value_list[IN].ShouldProcess())\
        {\
        evaluationOutput = m_evaluationOutput[IN];\
        controls = m_controls[IN];\
        }\
    VALUE_EXIST(evaluationOutput,controls,input_controls,IN,ID,Adj);\
    m_evaluationOutput[IN] = controls > 0 ? evaluationOutput : 0.0;\
    m_controls[IN] = controls;\
    m_input_controls[IN] = input_controls;\                                 
}
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
bsobaid
  • 955
  • 1
  • 16
  • 36
  • 1
    Macros are very rarely needed - over-use of macros is a "code smell". – Paul R Apr 30 '13 at 17:42
  • From the code you inherit, what is your judgement call? are the macros making things helpful? – andre Apr 30 '13 at 17:45
  • Macros can be useful shortcuts for hiding some messy/boilerplate/repeated code. An example would be generating some common declarations in classes through short (possibly parameterized) macros. However, as far as business logic is concerned, the much better job can be done by proper utilization of C++ arsenal (templates, function overloading, etc.) to achieve concise, readable, and safe code. So, in general, the answer is: **No**. – Alexander Shukaev Apr 30 '13 at 17:48
  • 1
    Are you sure it's C++ code, not C? – Joel Nelson Apr 30 '13 at 17:55
  • Yes, the code is and has always been C++. The usage of macros in my case is for business logic (if a> b then do c or populate an array etc) and not for conditional compilation. Many functions are also defined as #define MY_MACROS(A,B,C,D) GET##A() and then it goes on and on. – bsobaid Apr 30 '13 at 19:38
  • I dont find them useful at all. Code readability is non-existant and debugging with breakpoints is impossible hence code modification is extremely difficult. – bsobaid Apr 30 '13 at 19:39
  • The example you've given should probably be turned into its own function and marked as inline-able. A few indicators are opening a scope to declare variables (suggests it needs a scope and does something significant), and pulling in variables that aren't passed in the macro (dangerous, touchy, and much safer as a function taking references). – ssube May 01 '13 at 13:57

3 Answers3

9

In Effective C++, Scott Meyers notes in Item 2:

Prefer consts, enums, and inlines to #defines

Specifically referring to the practice of writing macros instead of functions, Meyers says:

Another common (mis)use of the #define directive is using it to implement macros that look like functions but don't incur the overhead of a function call.

Macros like this have so many drawbacks, just thinking about them is painful.

Fortunately, you don't have to put up with this nonsense. You can get all the efficiency of a macro plus all the predictable behavior and type safety of a regular function by using a template for an inline function.

[Real functions] obey scope and access rules. For example, it makes perfect sense to talk about an inline function that is private to a class. In general, there's just no way to do that with a macro.

To answer your questions specifically:

  • Some programmers write macros instead of functions to avoid the perceived overhead of a function call -- a dubious practice that often has little measurable benefit.
  • Programmers used to use the preprocessor to generate boilerplate code (and perhaps still do). In modern C++, use of the processor should be limited to #include and (maybe) #ifdef/#ifndef for conditional compilation.

As Meyers notes in closing:

It's not yet time to retire the preprocessor, but you should definitely give it long and frequent vacations.

Gnawme
  • 2,321
  • 1
  • 15
  • 21
6

No. They are absolutely not a general replacement for functions.

Macros are capable of things that the code itself is not, particular creating and modifying tokens (code) prior to compilation.

In exchange, they lose all type safety, and almost all syntactic sugar that may be available for actual code.

When you must do something that can't be done by code, or need code to exist conditionally (super-debug stuff like memory tracing), macros come in handy. They also have some value in providing more concise or more readable ways to do a particular thing (a common example is #define SUCCESS(x) (x >= 0). However, for anything requiring type-safety, exception-catching, or consisting of often-used code that doesn't have to mutate at compile-time, they are almost never appropriate. Most code that seems like it could be a macro can probably be more safely expressed in actual code, which is important for business logic.

The closest rule of thumb you can get is whether something needs to happen or change during compilation. If so, consider a macro, otherwise just code it up. Remember, templates count as code and can do quite a few things you might be tempted to use a macro for, but can be somewhat safer.

ssube
  • 47,010
  • 7
  • 103
  • 140
  • `#define ONE 1` is type safe. – Pete Becker Apr 30 '13 at 17:49
  • 2
    It doesn't provide any type *safety*. Macros operate on simple text search-and-replace (more or less), completely outside of the language's safety rules. Templates, for example, are compiled and type-checked (the error messages can be equally cryptic). Most of the interesting problems I've had while using macros were either type-related or clever new symbols from incorrect spacing/calling. – ssube Apr 30 '13 at 17:52
  • "There are things you can do that aren't safe" is not the same as "there is nothing you can do that is safe". If you want to argue against macros, at least make accurate arguments. Hyperbole hurts your credibility. – Pete Becker Apr 30 '13 at 17:54
  • 4
    Macros neither provide nor enforce any type safety. Standard rules apply after macros have been processed, but not to the macros; that ends up being a common issue with using them. Nowhere is it stated or implied that macros can't produce code conforming to the type system, but they themselves are entirely outside of the system and have no inherent safety checks. – ssube Apr 30 '13 at 18:01
  • Again: `#define ONE 1` is type safe. The assertion that macros **are not** type safe is wrong. Having inherent safety checks has very little to do with type safety. – Pete Becker Apr 30 '13 at 19:01
  • 1
    They are generally regarded as not type safe and should be treated as such regardless of that example. – Aleks Apr 30 '13 at 22:07
2

Long, cryptic anything is not good. If the macros exist for performance issues, you can achieve the same result with inline functions. If they are used for simplifying expressions, they could be refactored to be less cryptic and more easily maintained, if you have time and money for such an endeavor.

Bruce
  • 2,230
  • 18
  • 34