7

I am trying to document a header file which has different "overloads" when parsed by C or C++.

#ifdef __cplusplus

    /// myfuncA
    void myfuncA();

    /// MYMACRO_A
    #define MYMACRO_A() \
        myfuncA();

#else

    /// myfuncB
    void myfuncB();

    /// MYMACRO_B
    #define MYMACRO_B() \
        myfuncB();

#endif

I want all four of these functions and macros to be collected and individually documented by Doxygen. This is a sensible use-case in my application; these are C and C++ specific overloads to a core function, and the doc will specify which can be called by which language.

Alas, it is proving impossible to make Doxygen simultaneously document all four of them!

When my Doxyfile includes

ENABLE_PREPROCESSING = YES
MACRO_EXPANSION      = YES
SKIP_FUNCTION_MACROS = NO

then Doxygen will evaluate ifdef __cplusplus to 0 and document only myfuncB and MYMACRO_B.

Alternatively, when I set

ENABLE_PREPROCESSING = NO

then Doxygen will not document any macros at all, and so document only myfuncA and myfuncB.

Attempted solutions

  • I have tried every combination of MACRO_EXPANSION and EXPAND_ONLY_PREDEF, and exposing the macros to EXPAND_AS_DEFINED and PREDEFINED with no success.
  • I tried setting
    ENABLE_PREPROCESSING = YES
    PREDEFINED = ifdef __cplusplus=
    
    so that Doxygen would remove the #if branches before beginning parsing and evaluation, but this horrible hack is too complicated for my application.
  • I have tried foregoing Doxygen parsing the macros, and instead trying to document them completely fictitiously as "orphaned" functions - but it doesn't seem to work! Use of @def and @fn require Doxygen eventually encounter the real signature.
  • One could imagine a variant of PREDEFINED like PREDEFINED_IN_TURN which re-parses the source code for each set of preprocessor values. Then one could use
    ENABLE_PREPROCESSING = YES
    MACRO_EXPANSION      = YES
    SKIP_FUNCTION_MACROS = NO
    
    # crucially...
    PREDEFINED_IN_TURN = __cplusplus=0 __cplusplus=1
    
    but alas, there is no such key (and seems non-trivial to hack). Of course this would not work with my example code immediately which uses #ifdef over #if.
  • a functioning but invasive solution is to refactor the code such that every #if/#else branch is explored when a macro like DOXYGEN is defined. This requires removing all #else clauses:
    #if defined(__cplusplus) || defined(DOXYGEN)
       ... 
    #endif
    #if !defined(__cplusplus) || defined(DOXYGEN)
        ...
    #endif
    
    along with Doxyfile
    ENABLE_PREPROCESSING = YES
    MACRO_EXPANSION      = YES
    SKIP_FUNCTION_MACROS = NO
    
    # crucially
    PREDEFINED = DOXYGEN
    
    Alas this is a major and unacceptable change to the source-code in my application, and introduces bug vulnerability.

Unsatisfying solution

The only workaround I can think up to display all four functions/macros (without major refactor) is to spoof the macros with function signatures that we hide from the compilers (but not doxygen) using #if 0:

#ifdef __cplusplus

    /// myfuncA
    void myfuncA();

    #define MYMACRO_A() \
        myfuncA();

    #if 0
        /// MYMACRO_A
        void MYMACRO_A();
    #endif

#else

    /// myfuncB
    void myfuncB();

    #define MYMACRO_B() \
        myfuncB();

    #if 0
        /// MYMACRO_B
        void MYMACRO_B();
    #endif

#endif

The documentation will now include myfuncA, myfuncB, MYMACRO_A and MYMACRO_B but alas the latter two will be listed as functions rather than as macros.

Is this an unsupported use-case? Otherwise, what is a sensible, reliable method to achieve the documenting of all four entities, where the latter two are correctly reported as macros?

(I am using Doxygen 1.13.2 on MacOS)

1 Answer 1

4

One way you could do it is to add something like

PREDEFINED = DOXYGEN

to your .dox file, and then change your header files to be more like this:

#if defined(__cplusplus) || defined(DOXYGEN)
    /// myfuncA
    void myfuncA();

    /// MYMACRO_A
    #define MYMACRO_A() myfuncA();
#endif

#if !defined(__cplusplus) || defined(DOXYGEN)
    /// myfuncB
    void myfuncB();

    /// MYMACRO_B
    #define MYMACRO_B() myfuncB();
#endif
Sign up to request clarification or add additional context in comments.

3 Comments

Aha I had just arrived at this myself with the help of chatGPT - perhaps you did too, given use of the same DOXYGEN macro. Alas, as I've edited in my answer, that is a major and unacceptable refactor
I swear on a stack of C++ references that this answer came completely and wholly out of my own human brain. I do find ChatGPT useful in other circumstances, though :)
Hehe it's possible too ChatGPT was able to scrape your solution before I'd loaded it!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.