Is there an elegant solution to choose between callable and non-callable type in Variadic template in C ++ 14

I use a simple text formatter for the class. The main function in it would be to get a list of values ​​to be combined. Or, optionally, for cases where the parameters are not ostream friends, I accept the conversion function as the first parameter, which converts all the other parameters to std :: string.

The following code shows an idea, but it does not compile. For simplicity, I will output to cout in the example.

struct formater{
    template<typename P, typename... PS>
    void format(const P& p, const PS&... ps){
        if (std::is_convertible<P, std::function<void()>>::value){
            cout << p(ps...) << endl;
        } else {
            cout << p;
            log(ps...);
        }
    }
};

The reason the code does not compile is if P is callable, it will not be possible to output it to cout in the else branch, and if P is not callable, it will say that P is not callable and cannot receive ps ... parameters in then branch.

enable_if, (T F) , , .

static_if, .

, , P , SFINAE. , , P, (PS...) → std::string.

+4
2

. -, , , if - , -, , , , . , , .

. , ++ 17:

template<typename, typename = void>
struct is_callable : std::false_type {};

template<typename F, typename... Args>
struct is_callable<F(Args...), void_t<decltype(std::declval<F>()(std::declval<Args>()...))>> : std::true_type {};

std::enable_if:

struct Formatter {
    template<typename F, typename... Args>
    auto format(F func, Args&&... args) -> std::enable_if_t<is_callable_v<F(Args...)>> {
        std::cout << func(std::forward<Args>(args)...);
        std::cout << std::endl;
    }

    template<typename T>
    auto format(T&& value) -> std::enable_if_t<!is_callable_v<T()>> {
        std::cout << std::forward<T>(value);
        std::cout << std::endl;
    }
};

void_t :

template<typename...>
using void_t = void;

: Live at coliru

, ++ 17 constexpr if std::is_invocable:

struct Formatter {
    template<typename F, typename... Args
    void format(F&& param, Args&&... args) {
        if constexpr (std::is_invocable_v<F, Args...>) {
            std::cout << std::invoke(std::forward<F>(param), std::forward<Args>(args)...);
        } else {
            std::cout << std::forward<F>(param);
            log(std::forward<Args>(args)...);
        }
    }
};
+4

std::enable_if:

struct formater{
    template<typename P, typename... PS>
    std::enable_if<std::is_convertible<PARAM, std::function<void(PS...)>>::value, void>::type
    format(const PARAM& p, const PS&... ps){
        cout << p(ps...) << endl;

    }

    template<typename P, typename... PS>
    std::enable_if<!std::is_convertible<PARAM, std::function<void(PS...)>>::value, void>::type
    format(const PARAM& p, const PS&... ps){
        cout << p;
        log(ps...);

    }
};
+1

Source: https://habr.com/ru/post/1673901/


All Articles