Search code examples
c++c-preprocessorcode-generationforward-declaration

Using macros to define forward declarations


I am writing a pretty huge program with lots of templated functions. Naturally, the program has a long compile-time, which is why I wanted to use forward declaration. Now there are a lot of functions and I do not want to write the forward declaration for each single one of them. Even more so, I want to be able to add some functions without having to add the forward declarations manually.

A quick example:

#define max_dim 3
template bool match_any<1>();
template bool match_any<2>();
template bool match_any<3>();

If I set max_dim to another value, I do not want to manually add the additional forward declarations. Until now I have used a python-script, to just generate a file with all the forward declarations for me.

My goal now is to avoid the python-script and to do this using the cpp-preprocessor only (if this is possible). More concretly, I want to be able to just change the max_dim and all neccessary forward-declarations are generated.

I now that loops etc. are possible just with the preprocessor; what I do not know is how to actually make the preprocessor generate the declarations.


Solution

  • I'm not the right one to ask if what you want to do is possible in a better way, but this is definitely solvable using the preprocessor.

    I'll use file iteration, although other methods are also possible:

    // slot.h
    #ifndef A_0
    # include "a.h"
    #else
    # include "b.h"
    #endif
    
    
    // a.h
    #if (SLOT) & 1
    # define A_0 1
    #else
    # define A_0 0
    #endif
    #if (SLOT) & 2
    # define A_1 2
    #else
    # define A_1 0
    #endif
    #if (SLOT) & 4
    # define A_2 4
    #else
    # define A_2 0
    #endif
    #if (SLOT) & 8
    # define A_3 8
    #else
    # define A_3 0
    #endif
    
    #undef VAL
    #define VAL (A_3|A_2|A_1|A_0)
    
    #undef B_0
    #undef B_1
    #undef B_2
    #undef B_3
    
    
    // b.h
    #if (SLOT) & 1
    # define B_0 1
    #else
    # define B_0 0
    #endif
    #if (SLOT) & 2
    # define B_1 2
    #else
    # define B_1 0
    #endif
    #if (SLOT) & 4
    # define B_2 4
    #else
    # define B_2 0
    #endif
    #if (SLOT) & 8
    # define B_3 8
    #else
    # define B_3 0
    #endif
    
    #undef VAL
    #define VAL (B_3|B_2|B_1|B_0)
    
    #undef A_0
    #undef A_1
    #undef A_2
    #undef A_3
    
    
    
    // impl.cpp
    #ifndef VAL
    #define VAL 12
    #endif
    
    template bool match_any<VAL>();
    
    #define SLOT (VAL-1)
    #include "slot.h"
    #if VAL != 0
    #include __FILE__
    #endif
    

    (https://godbolt.org/z/csehns44j)

    If you need more than 4 bits to represent the value of max_dim, then you'd need to add a few lines to a.c and b.c. This uses self recursive includes, which only works for a few hundred iterations (without special compiler flags). To circumvent this, you can use the following structure:

    // iter1.h
    #if CONTINUE
    #include "iter2.h"
    #if CONTINUE
    #include "iter2.h"
    #if CONTINUE
    #include "iter2.h"
    #if CONTINUE
    #include "iter2.h"
    #if CONTINUE
    #include "iter2.h"
    // ...
    #endif
    #endif
    #endif
    #endif
    #endif
    
    // iter2.h
    #if CONTINUE
    #include "iter3.h"
    #if CONTINUE
    #include "iter3.h"
    #if CONTINUE
    #include "iter3.h"
    #if CONTINUE
    #include "iter3.h"
    #if CONTINUE
    #include "iter3.h"
    // ...
    #endif
    #endif
    #endif
    #endif
    #endif
    
    // iter3.c
    #if CONTINUE
    #include FILE
    #if CONTINUE
    #include FILE
    #if CONTINUE
    #include FILE
    #if CONTINUE
    #include FILE
    #if CONTINUE
    #include FILE
    // ...
    #endif
    #endif
    #endif
    #endif
    #endif
    
    
    
    // impl.cpp
    #ifndef VAL
    #define VAL 100
    #define FILE "impl.cpp"
    #endif
    
    template bool match_any<VAL>();
    
    #define SLOT (VAL-1)
    #define CONTINUE VAL != 0
    #include "slot.h"
    
    #ifndef ONCE
    #define ONCE
    #include "iter1.h"
    #endif
    

    (https://godbolt.org/z/h74jPb11c)

    With 3 iter.h files, of which each has 5 includes that results in 5^3 iterations. Expanding this should be trivial.