C++

C++ - Macros

Why Macros, Compiler Args

Posted by Rico's Nerd Cluster on January 13, 2023

Introduction

Macros are string substitution for code. It happens during the preprocessing phase. It is generally not encouraged in c++ nowadays. However, there are still applications where it could be handy / necessary

Here is the general compilation process

Application 1 - Linkage Specifier

1
2
3
4
5
6
7
8
9
10
#ifdef __cplusplus
    #define CV_IMPL extern "C"
#else
    #define CV_IMPL
#endif

CV_IMPL void cvFindExtrinsicCameraParams2(...){
    cvConvertPointsHomogeneous(objectPoints, matM);
    cvConvertPointsHomogeneous(imagePoints, _m);
}

This is effectively extern "C" void cvFindExtrinsicCameraParams2(...){} and void cvFindExtrinsicCameraParams2(...){}.

  • extern "C" is a “linkage specifier”. In C++, function names are mangled so they will be unique in the object code. E.g., foo() could become _fooid1(). This is also the underlying mechanism for function overloading. However, C does not have function overloading. So, when linking C code in C++, we need to make sure names of function symbols are unique, and exclude name mangling.

Compiler Args

Life cycle of adding a compiler arg:

  1. “turn on” a preprocessor‐symbol by passing -D<NAME> to the compiler.
  2. In the top-level CMake:
1
add_compile_definitions(PRINT_DEBUG_MSGS)
  1. In source code:
1
#ifdef PRINT_DEBUG_MSGS (or #if defined(PRINT_DEBUG_MSGS))
1
- If this is not defined, it won't be compiled

Custom Macros

1
2
#define SQR(ARG) ((x) * (x))
int y = FUNC(3);
  • Macros should be all caps.
  • Defines a macro SQR which textually substitute arg with its argument
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define TYPE_LIST(FUNC) \
    FUNC(LIVOX_AVIA) \
    FUNC(VELODYNE32)

enum class Type{
    #define RETURN_SELF(ARG) ARG,
    TYPE_LIST(RETURN_SELF)
    #undef RETURN_SELF
};

Type make_type(const std::string& t){
    #define RETURN_TYPE(arg) if (t == #arg) return Type::arg;
    TYPE_LIST(RETURN_TYPE)
    #undef RETURN_TYPE
    throw std::runtime_error("type string not found");
}

int main()
{
    make_type("LIVOX_AVIA");
    return 0;
}
  • Use \to switch lines
  • #define + #undefine is a good practice.
  • TYPE_LIST goes through LIVOX_AVIA and VELODYNE32, and subtitute RETURN_SELF there

  • # is a stringfication operator. RETURN_TYPE(LIVOX_AVIA) will substitute a string. if (t == 'LIVOX_AVIA')
    • But without #, #define RETURN_TYPE(arg) if (t == arg) return Type::arg;, we simply have textual substitution: if (t == LIVOX_AVIA)