template deduction failure in variadic macro

Viewed 111

the decltype(__VA_ARGS__) in my macro compiles when only for a single argument and not when multiple arguments are passed.

What I'm trying to achieve is to invoke a variadic function based on a runtime condition. The arguments to my printer could be expensive to compute and therefore I'd like to not evaluate those when my condition is false.

Here's a minimal example below. Any comments to rephrase this question/description are also appreciated.

#include <iostream>
#include <utility>
#include <functional> 

#define PRINT_IF(printer, cond, ...) if(cond) std::invoke(&Printer::print<decltype(__VA_ARGS__)>, printer, __VA_ARGS__)

// Some Fixed Printer Interface ... 
class Printer{
public:

    void print(){}
    
    template <typename T, typename... Types> 
    void print(T&& var1, Types&&... var2) 
    { 
        std::cout << var1 << std::endl ; 
        print(var2...);
    }
    
};


int main()
{
    Printer p;
    
    PRINT_IF(p, returns_false(), very_expensive_string()); // compiles and expensive operation skipped 
    PRINT_IF(p, true, 1); // compiles
    PRINT_IF(p, true, 1, 2, 3); // doesn't compile
    
    // The macro should also handle pointers.
    PRINT_IF(&p, true, 1, 2, 3);

    return 0;
}

1 Answers

You can do something like this to avoid having to find the template overload:

template <class T>
auto add_pointer(T&& t) {
    if constexpr (std::is_pointer_v<std::remove_reference_t<T>>) {
        return t;
    }
    else {
        return &t;
    }
}

#define PRINT_IF(printer, cond, ...) if(cond) add_pointer(printer)->print(__VA_ARGS__)

This lets ->print deduce the template arguments to avoid having to find an overload and just use add_pointer to convert printer to a pointer if it's not already.

Related