Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions stl/inc/format
Original file line number Diff line number Diff line change
Expand Up @@ -305,23 +305,15 @@ constexpr const _CharT* _Parse_format_specs(const _CharT* _Begin, const _CharT*
template <class _Ty, class _CharT = char>
struct formatter;

// TODO: test coverage
inline void _You_see_this_error_because_arg_id_is_out_of_range() noexcept {}

template <class _CharT>
class basic_format_parse_context {
public:
using char_type = _CharT;
using const_iterator = typename basic_string_view<_CharT>::const_iterator;
using iterator = const_iterator;

private:
basic_string_view<_CharT> _Format_string;
size_t _Num_args;
// The standard says this is size_t, however we use ptrdiff_t to save some space
// by not having to store the indexing mode. Below is a more detailed explanation
// of how this works.
ptrdiff_t _Next_arg_id = 0;

public:
constexpr explicit basic_format_parse_context(basic_string_view<_CharT> _Fmt, size_t _Num_args_ = 0) noexcept
: _Format_string(_Fmt), _Num_args(_Num_args_) {}
basic_format_parse_context(const basic_format_parse_context&) = delete;
Expand All @@ -333,8 +325,10 @@ public:
_NODISCARD constexpr const_iterator end() const noexcept {
return _Format_string.end();
}
constexpr void advance_to(const_iterator _It) {
_Format_string.remove_prefix(_It - begin());
constexpr void advance_to(const const_iterator _It) {
_Adl_verify_range(_It, _Format_string.end());
const auto _Diff = static_cast<size_t>(_It._Unwrapped() - _Format_string._Unchecked_begin());
_Format_string.remove_prefix(_Diff);
}

// While the standard presents an exposition only enum value for
Expand All @@ -343,18 +337,33 @@ public:
// _Next_arg_id > 0 means automatic
// _Next_arg_id == -1 means manual
constexpr size_t next_arg_id() {
if (_Next_arg_id >= 0) {
return _Next_arg_id++;
if (_Next_arg_id == -1) {
throw format_error("Can not switch from manual to automatic indexing");
}
throw format_error("Can not switch from manual to automatic indexing");

return static_cast<size_t>(_Next_arg_id++);
}
constexpr void check_arg_id(size_t _Id) {
(void) _Id;

constexpr void check_arg_id(const size_t _Id) {
if (_STD is_constant_evaluated()) {
if (_Id >= _Num_args) {
_You_see_this_error_because_arg_id_is_out_of_range();
}
}

if (_Next_arg_id > 0) {
throw format_error("Can not switch from automatic to manual indexing");
}
_Next_arg_id = -1;
}

private:
basic_string_view<_CharT> _Format_string;
size_t _Num_args;
// The standard says this is size_t, however we use ptrdiff_t to save some space
// by not having to store the indexing mode. Above is a more detailed explanation
// of how this works.
ptrdiff_t _Next_arg_id = 0;
};

// TODO: test coverage
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ tests\P0595R2_is_constant_evaluated
tests\P0607R0_inline_variables
tests\P0616R0_using_move_in_numeric
tests\P0631R8_numbers_math_constants
tests\P0645R10_text_formatting_death
tests\P0645R10_text_formatting_parse_contexts
tests\P0645R10_text_formatting_parsing
tests\P0660R10_jthread_and_cv_any
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0645R10_text_formatting_death/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst
30 changes: 30 additions & 0 deletions tests/std/tests/P0645R10_text_formatting_death/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#define _CONTAINER_DEBUG_LEVEL 1

#include <assert.h>
#include <format>
#include <string_view>

#include <test_death.hpp>
using namespace std;

void test_case_advance_no_range() {
const auto format_string = "First {} and second {} and third {}"sv;
const auto other_format_string = "something"sv;
basic_format_parse_context context{format_string};
context.advance_to(other_format_string.begin());
}

int main(int argc, char* argv[]) {
std_testing::death_test_executive exec;

#if _ITERATOR_DEBUG_LEVEL != 0
exec.add_death_tests({
test_case_advance_no_range,
});
#endif // _ITERATOR_DEBUG_LEVEL != 0

return exec.run(argc, argv);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_matrix.lst
RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst
101 changes: 99 additions & 2 deletions tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,108 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <assert.h>
#include <concepts>
#include <cstddef>
#include <format>
#include <memory>
#include <string_view>
#include <type_traits>

// TODO: fill in tests
using namespace std;

template <class CharType>
constexpr auto get_input() {
if constexpr (same_as<CharType, char>) {
return "First {} and second {} and third {}"sv;
} else {
return L"First {} and second {} and third {}"sv;
}
}

template <class CharType>
constexpr bool ensure_is_constant_expression(const bool should_be_constant_expression) {
basic_format_parse_context<CharType> context{get_input<CharType>(), 1};
if (should_be_constant_expression) {
context.check_arg_id(0);
} else {
context.check_arg_id(1);
}

return is_constant_evaluated();
}

const bool check_arg_id_is_constexpr_char = ensure_is_constant_expression<char>(true);
const bool check_arg_id_not_constexpr_char = !ensure_is_constant_expression<char>(false);
const bool check_arg_id_is_constexpr_wchar = ensure_is_constant_expression<wchar_t>(true);
const bool check_arg_id_not_constexpr_wchar = !ensure_is_constant_expression<wchar_t>(false);

template <class CharType>
constexpr bool test_basic_format_parse_context() {
static_assert(!is_copy_constructible_v<basic_format_parse_context<CharType>>);
static_assert(!is_copy_assignable_v<basic_format_parse_context<CharType>>);

const auto format_string = get_input<CharType>();
{ // iterator interface
basic_format_parse_context<CharType> context{format_string};
const same_as<typename basic_string_view<CharType>::const_iterator> auto b = context.begin();
assert(b == format_string.begin());
static_assert(noexcept(context.begin()));

const same_as<typename basic_string_view<CharType>::const_iterator> auto e = context.end();
assert(e == format_string.end());
static_assert(noexcept(context.end()));

const auto new_position = format_string.begin() + 5;
context.advance_to(new_position);
assert(to_address(context.begin()) == to_address(new_position));
assert(to_address(context.end()) == to_address(e));
}

{ // arg_id
basic_format_parse_context<CharType> context{format_string};
const same_as<size_t> auto first_arg_id = context.next_arg_id();
assert(first_arg_id == 0);

const same_as<size_t> auto second_arg_id = context.next_arg_id();
assert(second_arg_id == 1);

if (!is_constant_evaluated()) {
try {
context.check_arg_id(0);
} catch (const format_error& e) {
assert(e.what() == "Can not switch from automatic to manual indexing"sv);
}
}
}

{ // check_arg_id
basic_format_parse_context<CharType> context{format_string, 3};
context.check_arg_id(0);
context.check_arg_id(1);
context.check_arg_id(2); // intentional duplicates to check whether this is ok to call multiple times
context.check_arg_id(1);
context.check_arg_id(0);

if (!is_constant_evaluated()) {
try {
context.next_arg_id();
} catch (const format_error& e) {
assert(e.what() == "Can not switch from manual to automatic indexing"sv);
}
}
}

return true;
}

int main() {
return 0;
test_basic_format_parse_context<char>();
test_basic_format_parse_context<wchar_t>();
static_assert(test_basic_format_parse_context<char>());
static_assert(test_basic_format_parse_context<wchar_t>());

assert(check_arg_id_is_constexpr_char);
assert(check_arg_id_not_constexpr_char);
assert(check_arg_id_is_constexpr_wchar);
assert(check_arg_id_not_constexpr_wchar);
}