7

I would like to know if there is built-in checkers in std which checks if a type is printable via operator<< or if it is formattable via std::format.

I tried to search it and could not find built-in checker to check if operator<< can be called on a type. I only found std::formattable<T> and could not figure out how to use it.

I want to use built-in type traits and concepts from std, I do not want to create my own utility.

What I would like to be able to do is:

template<typename DATA_T>
struct std::formatter<MyClass<DATA_T>> : std::formatter<std::string>
{
    static auto format(const MyClass<DATA_T>& my_class, std::format_context& ctx)
    {
        std::string result = std::format("Name: {}\n", my_class.name());

        // Check if the data it holds printable or formattable and add it.
        // Should be replaced with correct usage
        if constexpr (std::formattable<DATA_T>) 
        {
            result += std::format("Data: {}", my_class.data());
        }
        // Should be replaced with correct std functionality
        else if constexpr (std::ostreambale<DATA_T>) 
        {
            std::ostringstream oss;
            oss << my_class.data();
            result += "Data: ";
            result += oss.str();
        }

        result += std::format("\nLocation: {}", my_class.location());

        return std::format_to(ctx.out(), "{}", result);
    }
};
2
  • formattable is connected to std::format, and has nothing to do with operator<<. Commented Mar 15 at 15:26
  • But you could test for "streamability" with something like requires { oss << my_class.data(); }. Commented Mar 15 at 15:27

1 Answer 1

11

I tried to search it and could not find built-in checker to check if operator<< can be called on a type. I only found std::formattable<T> [...]

Yes, that is correct. For streamability via operator<<, there is no built-in trait in the standard library; therefore unfortunately, you must define your own check.

// Custom concept to check if a type is streamable via operator<<
template<typename T>
concept OStreamable = requires(std::ostream& os, const T& t) 
{
    { os << t } -> std::convertible_to<std::ostream&>;
};

With that update, you might do:

template<typename DATA_T>
struct std::formatter<MyClass<DATA_T>> : std::formatter<std::string> 
{
    auto format(const MyClass<DATA_T>& my_class, std::format_context& ctx) const
    {
        std::string result = std::format("Name: {}\n", my_class.name());

        // Check if DATA_T is formattable
        if constexpr (std::formattable<DATA_T, char>)
        {
            result += std::format("Data: {}", my_class.data());
        }
        // Use custom OStreamable concept for streamability
        else if constexpr (OStreamable<DATA_T>) {
            std::ostringstream oss;
            oss << my_class.data();
            result += "Data: " + oss.str();
        }

        result += std::format("\nLocation: {}", my_class.location());
        return std::format_to(ctx.out(), "{}", result);
    }
};

((See live demo))

Sign up to request clarification or add additional context in comments.

6 Comments

I am trying to use this approach with a custom type (FormatOnlyType) that I created a std::formatter specialization. I can use std::println() but if constexpr (std::formattable<FormatOnlyType, char>) returns false. Does std::formattable<std::errc, char> only checks if std::formatter is provided by the standard library? What is the easiest way to define a concept which also works with user defined std::formatter specializations? Example: gcc.godbolt.org/z/9jb631hhr
@sakcakoca Yes, you are right. The std::formattable<..., char> only checks if built‐in ones provide std::formatter. In that case, you might need to write your version of the formattable concept: gcc.godbolt.org/z/nchWqco5r
It looks that in certain situations, OStreamable<StreamOnlyType> can be true, while oss << my_class.data(); fails to compile: gcc.godbolt.org/z/6Ga3GdYWf Can we adjust the concept somehow?
@Fedor The issue you are facing looks similar to stackoverflow.com/q/79512787/15814430 . I was able to fix it by; instead of implementing the std::formatter for MyClass and reusing it in std::ostream& operator<<(std::ostream& ostr, const MyClass<DATA_T>& my_class), I did it otherwise around and implemented std::ostream& operator<<(std::ostream& ostr, const MyClass<DATA_T>& my_class) and reused it in std::formatter. In this way, I do not need to use using ::operator<< statement before oss << my_class.data(). See: gcc.godbolt.org/z/fPsTPhrnW
@JeJo The concept you have shared returns true for if constexpr (MyFormattable<std::errc, char>) even though there is no std::formatter specialization is created for std::errc and it is not possible to call std::format on it. See: gcc.godbolt.org/z/j1aanbvjo
|

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.