A few use of macros in C/C++

If you’ve written C/C++ code, you must know and probably have used macros. You may have defined handy macros to compute min and max. However, there are more ways macros can be used in C/C++ projects. I will try to describe a few which I frequently use in this post.

Header guard

The most frequent use of macro for me is definitely acting as the header guard. In C/C++ code, you cannot redefine the same function or variable twice. For example, if you include the header stdio.h and you include another header some_header.h, and the some_header.h includes stdio.h, you have a problem. The contents of stdio.h will appear twice! To prevent that, we add “header guards” to all header files.

#ifndef GUARD
#define GUARD

// your normal definitions
void func1();
void func2();

#endif

When this header gets included for the first time, GUARD will be defined. If this header gets included again, its content will not appear for the second time because #ifndef will evaluate to false. I’m just using the name GUARD as an example. Usually, I name the macro according to the location of the file, e.g. DIR1_DIR2_FILE1_H_. Header guard should be a must for all headers you write.

Printing source file location for debugging

When you print debugging information, it would be extremely helpful to include the file name and line number of that printing statement. This helps you to quickly locate where the error happened. Luckily, you don’t have to do this manually. The preprocessor provides pre-defined macros __FILE__ and __LINE__ for this purpose.

#include <stdio.h>

int main()
{
    printf("[%s:%d] Error.\n", __FILE__, __LINE__);
    return 0;
}

This will output the following line (I named the file test.c).

[test.c:5] Error.

You can also use __func__ or __FUNCTION__ to get the function name. __FUNCTION__ is defined by GCC, while __func__ is introduced in the C99 standard.

Token concatenation

Suppose you want to define several functions whose name all starts with my_function_prefix_. One obvious choice is to manually write all functions names. An easier way to achieve this is to use the ## operator during preprocessing.

#define FUNC(name) my_function_prefix_ ## name

void FUNC(foo)();
void FUNC(bar)();

Inspecting the preprocessed file with gcc -E, we can see the correct function names being generated.

void my_function_prefix_foo();
void my_function_prefix_bar();

Besides being more convenient, this method also has the advantage that you only need to change one line if you want to change the prefix for all functions.

Sometimes, the name what you pass into FUNC may also be a macro and you would like to expand it first. In this case, you must use 2 layers of macro.

#define FUNC_(name) my_function_prefix_ ## name
#define FUNC(name) FUNC_(name)

This is because when using ##, macros will not be expanded. Therefore, we need FUNC to let name expand first and then pass the expanded content to an “internal” macro FUNC_. For example, if you have the following code.

#define FUNC_(name) my_function_prefix_ ## name
#define FUNC(name) FUNC_(name)

#define MACRO_A foo
#define MACRO_B bar

void FUNC(MACRO_A)();
void FUNC_(MACRO_B)();

This is what you see after preprocessing.

void my_function_prefix_foo();
void my_function_prefix_MACRO_B();

Selective compilation

In certain cases, you would like to selectively compile only part of a source file. For example, you may need to include different header depending on what OS is used. You may want your code to work in both C and C++. Or maybe you want to disable the code for an optional feature unless the user requests for it. We can use macro to achieve this.

#include <stdio.h>

int main()
{
#ifdef OPTIONAL
    printf("Optional feature enabled.\n");
#else
    printf("Optional feature not enabled.\n");
#endif
    return 0;
}

If you compile the code without any flags, Optional feature not enabled. will be printed. However, if you add the -DOPTIONAL flag, which defines the OPTIONAL macro, Optional feature enabled. will be printed instead. This is extremely common in large projects where there are all kinds of optinoal features.

Conclusion

In this post, I described four ways how macros are used in C/C++ projects. These are all what I commonly see in C/C++ code. Therefore, I hope knowing all these help you write better code. However, do use macros with caution. Sometimes, it may make the code harder to understand for people who tries to learn about your code.

References

 
comments powered by Disqus