Skip to content

<format>: Can't call format_to in a formatter specialization #1961

Closed

Description

When specializing std::formatter is can be useful to call std::format_to(format_context.out(), ...) to actually do the formatting.
In fact the Working Draft includes an example that does just that.
But when attempting to do so I get the following error: format(1816,15): error: no member named 'resize' in 'std::_Fmt_buffer<char>'.

Test case

#include <format>

struct wrapper {
    int value;
};

template<typename CharT>
struct std::formatter<wrapper, CharT> {
    auto parse(auto &context) const {
        auto it = context.begin();
        if (it != context.end() && *it != CharT{'}'}) {
            throw format_error{"invalid format specification"};
        }
        return it;
    }

    auto format(const wrapper wrap, auto &context) const {
        return format_to(context.out(), "{}", wrap.value);
    }
};

auto main() -> int {
    std::format("{}", wrapper{123});
}

The full error message from Clang (MSVC fails the in exact same way)

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\format(1816,15): error: no member named 'resize' in 'std::_Fmt_buffer<char>'
        _Cont.resize(_Capacity);
        ~~~~~ ^
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\format(1824,14): note: in instantiation of member function 'std::_Fmt_iterator_buffer<std::back_insert_iterator<std::_Fmt_buffer<char>>, char>::_Grow' requested here
    explicit _Fmt_iterator_buffer(back_insert_iterator<_Container> _Out, ptrdiff_t = 0)
             ^
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\format(2897,43): note: in instantiation of member function 'std::_Fmt_iterator_buffer<std::back_insert_iterator<std::_Fmt_buffer<char>>, char>::_Fmt_iterator_buffer' requested here
    _Fmt_iterator_buffer<_OutputIt, char> _Buf(_STD move(_Out));
                                          ^
main.cpp(87,16): note: in instantiation of function template specialization 'std::format_to<std::back_insert_iterator<std::_Fmt_buffer<char>>, int>' requested here
        return format_to(context.out(), "{}", wrap.value);
               ^
main.cpp(92,10): note: in instantiation of function template specialization 'std::format<wrapper>' requested here
    std::format("{}", wrapper{123});
         ^

I'm pretty sure the problem is that the _Fmt_iterator_buffer<back_insert_iterator<_Container>, ...> specialization is not sufficiently constrained.
_Fmt_iterator_buffer::_Grow calls _Cont::resize here:

STL/stl/inc/format

Lines 1816 to 1818 in 4c862ee

void _Grow(size_t _Capacity) final {
_Cont.resize(_Capacity);
this->_Set(_RANGES data(_Cont), _Capacity);

But the constraint on the specialization does not check for resize being available:

STL/stl/inc/format

Lines 1800 to 1803 in 4c862ee

requires _RANGES contiguous_range<_Container> && _RANGES sized_range<_Container>
&& requires(_Container& _Cont, const _RANGES range_value_t<_Container>& _Val) {
_Cont.push_back(_Val);
}

Considering that the specialization in question is meant to optimize for contiguous output buffers and _Fmt_buffer is contiguous, it would seem reasonable to add a resize member function to _Fmt_buffer (or maybe there should even be a separate specialization for _Fmt_buffer).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfixedSomething works now, yay!formatC++20/23 format

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

        翻译: