How can I reference function prototype parameter default values in a #define in C++?

108 views Asked by At

Suppose I have a function prototype which defaults several parameters:

bool debug_log(char* text, int len,
               bool   log_always = true,    // Defaults to yes, log
               SRgba* backRgba   = NULL,    // Use default color
               SRgba* foreRgba   = NULL);   // Use default color

If I create a few #defines which reference that function, but populate in color values for their logging types, how can I get that log_always parameter's default value to come through based on how it's defined in the prototype?

#define debug_log_err(a, b) debug_log(a, b, \
                                      true, \
                                      &errBackRgba, \
                                      &errForeRgba)
    
#define debug_log_block(a, b) debug_log(a, b, \
                                        true, \
                                        &blockBackRgba, \
                                        &blockForeRgba)

#define debug_log_highlight(a, b) debug_log(a, b, \
                                            true, \
                                            &highlightBackRgba, \
                                            &highlightForeRgba)

In these cases, everything's in sync presently because the 3rd parameter is passing in true, which is how the prototype defines the default parameter. But if I later go in and decide to set log_always to false, then unless I remember to also update my #define usages, they will then be stale and out of sync.

I'd like some kind of away to do something like this with something like the @ character, which tells the compiler to use whatever the prototype's default value is there, rather than me having to hard-coding it.

#define debug_log_err(a, b) debug_log(a, b, \
/* Let the compiler fill in */        @, \
                                      &errBackRgba, \
                                      &errForeRgba)

#define debug_log_block(a, b) debug_log(a, b, \
/* Let the compiler fill in */          @, \
                                        &blockBackRgba, \
                                        &blockForeRgba)

#define debug_log_highlight(a, b) debug_log(a, b, \
/* Let the compiler fill in */              @, \
                                            &highlightBackRgba, \
                                            &highlightForeRgba)

Does such an ability exist? And if not, this would seem to be a shortcoming in C++, similar to the inability to specify just a few parameter and let the rest all be auto-populated with their defaults when provided.

UPDATE: What I'm getting at is that #defines like this expand out to whatever they expand out to, which is then compiled. So perhaps the question I'm asking is ... is there a way to reference in a function use the intervening default parameters, and I know the answer to that is no. So, this question is mostly commentary that I think that should change and the ability should be added to C++.

2

There are 2 answers

3
rgnt On

Does such an ability exist? And if not, this would seem to be a shortcoming in C++, similar to the inability to specify just a few parameter and let the rest all be auto-populated with their defaults when provided.

Compiler can't guess the position of arguments that you provided, that's why the optional arguments have to be last. It's convention.

Sure, your point is to add an operator for the compiler to know that you are trying to substitute default value provided by function prototype but it's kinda impossible for compiler to actually know what function you are trying to call


// the operator for argument substitution is @

int log(int level, bool flag = false, int color = 0);
int log(int level, int color = 0);

int main() {
    log(0, @, 1); // Obviously, there's only one function, so this call is clear
    log(0, @);    // But considering overloading, this call is ambiguous 
    return 0;
}

so you can see where the operator comes short and could lead to trivial issues when linking.

As for the solution, why not just move the log_always to the back of parameters?

bool debug_log(char* text, int len,
               SRgba* backRgba   = NULL,    // Use default color
               SRgba* foreRgba   = NULL,    // Use default color
               bool   log_always = true,    // Defaults to yes, log
);   

#define debug_log_err(a, b) debug_log(a, b, \
                                      &errBackRgba, \
                                      &errForeRgba, \
                                      true)
    
#define debug_log_block(a, b) debug_log(a, b, \
                                        &blockBackRgba, \
                                        &blockForeRgba)

#define debug_log_highlight(a, b) debug_log(a, b, \
                                            &highlightBackRgba, \
                                            &highlightForeRgba)
0
Eugene On

To solve your problem, replace true in the function prototype and the macros with a constant:

    const bool log_always_default = true;  // Defaults to yes, log

    bool debug_log(char* text, int len,
                   bool   log_always = log_always_default,    
                   SRgba* backRgba   = NULL,    // Use default color
                   SRgba* foreRgba   = NULL);   // Use default color

    #define debug_log_err(a, b) debug_log(a, b, \
                                          log_always_default, \
                                          &errBackRgba, \
                                          &errForeRgba)
        
    #define debug_log_block(a, b) debug_log(a, b, \
                                            log_always_default, \
                                            &blockBackRgba, \
                                            &blockForeRgba)
    
    #define debug_log_highlight(a, b) debug_log(a, b, \
                                                log_always_default, \
                                                &highlightBackRgba, \
                                                &highlightForeRgba)

Now if you change log_always_default to false, it will affect all your macros.

As others pointed out, there is no reason to use macros here. You can replace them with inline functions, like:

inline bool debug_log_err(char* text, int len)
{
    return debug_log(text, len, log_always_default, &errBackRgba, &errForeRgba);
}