What are C macros useful for? What are C macros useful for? c c

What are C macros useful for?


I end up having to remember what the macro is and substitute it in my head as I read.

That seems to reflect poorly on the naming of the macros. I would assume you wouldn't have to emulate the preprocessor if it were a log_function_entry() macro.

The ones that I have encountered that were intuitive and easy to understand were always like little mini functions, so I always wondered why they weren't just functions.

Usually they should be, unless they need to operate on generic parameters.

#define max(a,b) ((a)<(b)?(b):(a))

will work on any type with an < operator.

More that just functions, macros let you perform operations using the symbols in the source file. That means you can create a new variable name, or reference the source file and line number the macro is on.

In C99, macros also allow you to call variadic functions such as printf

#define log_message(guard,format,...) \   if (guard) printf("%s:%d: " format "\n", __FILE__, __LINE__,__VA_ARGS_);log_message( foo == 7, "x %d", x)

In which the format works like printf. If the guard is true, it outputs the message along with the file and line number that printed the message. If it was a function call, it would not know the file and line you called it from, and using a vaprintf would be a bit more work.


This excerpt pretty much sums up my view on the matter, by comparing several ways that C macros are used, and how to implement them in D.

copied from DigitalMars.com

Back when C was invented, compiler technology was primitive. Installing a text macro preprocessor onto the front end was a straightforward and easy way to add many powerful features. The increasing size & complexity of programs have illustrated that these features come with many inherent problems. D doesn't have a preprocessor; but D provides a more scalable means to solve the same problems.

Macros

Preprocessor macros add powerful features and flexibility to C. But they have a downside:

  • Macros have no concept of scope; they are valid from the point of definition to the end of the source. They cut a swath across .h files, nested code, etc. When #include'ing tens of thousands of lines of macro definitions, it becomes problematical to avoid inadvertent macro expansions.
  • Macros are unknown to the debugger. Trying to debug a program with symbolic data is undermined by the debugger only knowing about macro expansions, not the macros themselves.
  • Macros make it impossible to tokenize source code, as an earlier macro change can arbitrarily redo tokens.
  • The purely textual basis of macros leads to arbitrary and inconsistent usage, making code using macros error prone. (Some attempt to resolve this was introduced with templates in C++.)
  • Macros are still used to make up for deficits in the language's expressive capability, such as for "wrappers" around header files.

Here's an enumeration of the common uses for macros, and the corresponding feature in D:

  1. Defining literal constants:

    • The C Preprocessor Way

      #define VALUE 5
    • The D Way

      const int VALUE = 5;
  2. Creating a list of values or flags:

    • The C Preprocessor Way

      int flags:#define FLAG_X  0x1#define FLAG_Y  0x2#define FLAG_Z  0x4...flags |= FLAG_X;
    • The D Way

      enum FLAGS { X = 0x1, Y = 0x2, Z = 0x4 };FLAGS flags;...flags |= FLAGS.X;
  3. Setting function calling conventions:

    • The C Preprocessor Way

      #ifndef _CRTAPI1#define _CRTAPI1 __cdecl#endif#ifndef _CRTAPI2#define _CRTAPI2 __cdecl#endifint _CRTAPI2 func();
    • The D Way

      Calling conventions can be specified in blocks, so there's no need to change it for every function:

      extern (Windows){    int onefunc();    int anotherfunc();}
  4. Simple generic programming:

    • The C Preprocessor Way

      Selecting which function to use based on text substitution:

      #ifdef UNICODEint getValueW(wchar_t *p);#define getValue getValueW#elseint getValueA(char *p);#define getValue getValueA#endif
    • The D Way

      D enables declarations of symbols that are aliases of other symbols:

      version (UNICODE){    int getValueW(wchar[] p);    alias getValueW getValue;}else{    int getValueA(char[] p);    alias getValueA getValue;}

There are more examples on the DigitalMars website.


They are a programming language (a simpler one) on top of C, so they are useful for doing metaprogramming in compile time... in other words, you can write macro code that generates C code in less lines and time that it will take writing it directly in C.

They are also very useful to write "function like" expressions that are "polymorphic" or "overloaded"; e.g. a max macro defined as:

#define max(a,b) ((a)>(b)?(a):(b))

is useful for any numeric type; and in C you could not write:

int max(int a, int b) {return a>b?a:b;}float max(float a, float b) {return a>b?a:b;}double max(double a, double b) {return a>b?a:b;}...

even if you wanted, because you cannot overload functions.

And not to mention conditional compiling and file including (that are also part of the macro language)...