Как проверить, что тип T поддерживается iostream, format или to_chars?

Для начала я попробовал использовать std::is_invocable_v:

struct test_iostream {
    template <typename T>
    std::string operator()(T x, const std::string& low) {
        std::ostringstream o;
        o << x;
        return o.str();
    }
}
template <typename F, typename T>
std::string test_cvt_one(testfloat_t x, const std::string &low) {
    if constexpr (std::is_invocable_v<F, T, decltype(low)>) {
        F f;
        std::string str = f(T(x), low);
        // ...
    }
}
...
low = test_cvt_one<F, std::float128_t>(x, low);

К сожалению это не работает, по крайней мере, clang/gcc/vs считают f(T(x), low) хорошим выражением, но вычислить его не могут.


Ответы (1 шт):

Автор решения: Serge3leo

Удобнее использовать concept (по аналогии с https://ru.stackoverflow.com/a/1579358/430734 и https://ru.stackoverflow.com/a/1579345/430734), так получается короче:

template <typename T>
concept OStreamable = requires(T x, std::ostream& o) {
    {o << x} -> std::same_as<std::ostream&>;
};
template<typename T>
concept TO_Charsable =
    #if defined(__clang__) && defined(_LIBCPP_VERSION)
        // https://github.com/llvm/llvm-project/issues/62282
        // См. примечание к P0067R5 в https://libcxx.llvm.org/Status/Cxx17.html
        (!std::is_same_v<T, long double> ||
         std::numeric_limits<long double>::digits ==
         std::numeric_limits<double>::digits) &&
    #endif
requires(T x, char* first, char* last) {
    {std::to_chars(first, last, x)} -> std::same_as<std::to_chars_result>;
};
template <typename T>
concept Formattable =
    #if defined(__clang__) && defined(_LIBCPP_VERSION)
        (!std::is_same_v<T, long double> ||
         std::numeric_limits<long double>::digits ==
         std::numeric_limits<double>::digits) &&
    #endif
    std::formattable<T, char>;
    // Паллиатив: std::is_default_constructible_v<std::formatter<T>>;

Используя, например, так:

struct test_iostream {
    template <OStreamable T>
    std::string operator()(T x, const std::string& low) {
    ...
    }
};
// или этак:
static_assert(OStreamable<std::string>);

Тесты (примеры использования)

P.S.

К сожалению, в libc++ (проект llvm, бывает, что используется с clang, часто, на FreeBSD/macOS), перегрузки std::formatter<long double> и to_chars() имеются, но некорректные. Поэтому пришлось чистый std::formattable<T, char> немного замутить.

→ Ссылка