Skip to content

Commit

Permalink
<format> : basic_format_stringクラスを追加 (close #1097)
Browse files Browse the repository at this point in the history
  • Loading branch information
faithandbrave committed Feb 6, 2023
1 parent 4d92044 commit f770ec5
Show file tree
Hide file tree
Showing 12 changed files with 307 additions and 42 deletions.
8 changes: 8 additions & 0 deletions reference/format.md
Expand Up @@ -51,6 +51,14 @@
| [`format_args`](format/basic_format_args.md) | `basic_format_args`のマルチバイト文字列版 (type-alias) | C++20 |
| [`wformat_args`](format/basic_format_args.md) | `basic_format_args`のワイド文字列版 (type-alias) | C++20 |


## 書式文字列

| 名前 | 説明 | 対応バージョン |
|------|------|----------------|
| [`basic_format_string`](format/basic_format_string.md) | 書式のコンパイル時文字列クラス (class template) | C++23 |


## 例外

| 名前 | 説明 | 対応バージョン |
Expand Down
114 changes: 114 additions & 0 deletions reference/format/basic_format_string.md
@@ -0,0 +1,114 @@
# basic_format_string
* format[meta header]
* class[meta id-type]
* std[meta namespace]
* cpp23[meta cpp]

```cpp
namespace std {
template <class charT, class... Args>
struct basic_format_string;

template <class... Args>
using format_string = basic_format_string<char, type_identity_t<Args>...>;

template <class... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
}
```
* type_identity_t[link /reference/type_traits/type_identity.md]
## 概要
`basic_format_string`クラスは、コンパイル時の書式文字列を表すクラスである。
C++20までは説明専用クラス`basic-format-string`として定義されていたが、C++23からはユーザーが利用できる正式な機能として定義される。
このクラスは、書式文字列のコンパイル時チェックを行う。
```cpp
auto str = std::format("{:d}", "I am not a number"); // コンパイルエラー!
// 書式文字列は「d」で整数を要求しているが、引数として文字列が渡された
```

このクラスのコンストラクタは、パラメータの書式文字列と、クラステンプレートパラメータの`Args...`を照合し、

- 書式文字列の型チェック
- 開きカッコと閉じカッコの一致
- その他、型ごとに許可されたオプションが正しいか

などをチェックする。

C++23で説明専用クラスでなく正式な機能として定義されたことにより、以下のように書式化を含むロギングなどをユーザーが定義できるようになる。

```cpp
template <typename... Args>
void log(std::format_string<Args...> s, Args&&... args) {
if (logging_enabled) {
log_raw(std::format(s, std::forward<Args>(args)...));
}
}
```
* std::format[link /reference/format/format.md]
* std::forward[link /reference/utility/forward.md]
## メンバ関数
| 名前 | 説明 | 対応バージョン |
|-----------------|----------------|----------------|
| [`(constructor)`](basic_format_string/op_constructor.md) | コンストラクタ | C++23 |
| [`get`](basic_format_string/get.md) | 書式文字列を取得する | C++23 |
## 例
```cpp example
#include <iostream>
#include <format>
#include <chrono>
thread_local bool logging_enabled = true;
// 現在時刻付きで、文字列フォーマットしてログ出力する
template <typename... Args>
void log(std::format_string<Args...> s, Args&&... args) {
if (!logging_enabled) {
return;
}
namespace chrono = std::chrono;
auto now = chrono::floor<chrono::seconds>(chrono::system_clock::now());
std::cout << std::format("{}: {}",
chrono::zoned_time{"Asia/Tokyo", now},
std::format(s, std::forward<Args>(args)...)
) << std::endl;
}
int main()
{
log("Hello {} World", 42);
}
```
* std::format_string[color ff0000]
* chrono::system_clock[link /reference/chrono/system_clock.md]
* now[link /reference/chrono/system_clock/now.md]
* chrono::floor[link /reference/chrono/time_point/floor.md]
* chrono::seconds[link /reference/chrono/duration_aliases.md]
* chrono::zoned_time[link /reference/chrono/zoned_time.md]
* std::forward[link /reference/utility/forward.md]

### 出力
```
2023-02-06 10:46:53: Hello 42 World
```

## バージョン
### 言語
- C++23

### 処理系
- [Clang](/implementation.md#clang): ??
- [GCC](/implementation.md#gcc): 13.1
- [Visual C++](/implementation.md#visual_cpp): ??

## 参照
- [P2508R1 Expose `std::basic-format-string<charT, Args...>`](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2508r1.html)
52 changes: 52 additions & 0 deletions reference/format/basic_format_string/get.md
@@ -0,0 +1,52 @@
# get
* format[meta header]
* function[meta id-type]
* std[meta namespace]
* basic_format_string[meta class]
* cpp23[meta cpp]

```cpp
constexpr basic_string_view<charT> get() const noexcept; // (1)
```

## 概要
書式文字列を取得する。


## 戻り値
メンバ変数として`basic_string_view<charT> str;`が定義されるとして、

```cpp
return str;
```


##
```cpp example
#include <cassert>
#include <format>

template <class... Args>
void f(std::format_string<Args...> fmt, Args&&...) {
assert(fmt.get() == "Hello {} World");
}

int main() {
f("Hello {} World", 42);
}
```
* fmt.get()[color ff0000]
## バージョン
### 言語
- C++23
### 処理系
- [Clang](/implementation.md#clang): ??
- [GCC](/implementation.md#gcc): 13.1
- [Visual C++](/implementation.md#visual_cpp): ??
## 参照
- [P2508R1 Expose `std::basic-format-string<charT, Args...>`](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2508r1.html)
92 changes: 92 additions & 0 deletions reference/format/basic_format_string/op_constructor.md
@@ -0,0 +1,92 @@
# コンストラクタ
* format[meta header]
* function[meta id-type]
* std[meta namespace]
* basic_format_string[meta class]
* cpp23[meta cpp]

```cpp
template <class T>
consteval basic_format_string(const T& s);
```
## 概要
`basic_format_string`オブジェクトを構築する。
## テンプレートパラメータ制約
- `const T&`は[`convertible_to`](/reference/concepts/convertible_to.md)`<`[`basic_string_view`](/reference/string_view/basic_string_view.md)`<charT>>`のモデルであること
## 効果
メンバ変数として`basic_string_view<charT> str;`が定義されるとして、`str(s)`で初期化する。
## 備考
- この関数の呼び出しは、`str`が`args`の書式文字列であるような`Args`型の`args`が存在しない限り、コア定数式ではない
- 意味 : 書式文字列が引数列`args`と合わせて正しくなければ、定数式評価 (`consteval`) が実行できずコンパイルエラーとなる
- 以下のようなコードはコンパイルエラーとなる:
```cpp
int main() {
auto str = std::format("{:d}", "I am not a number"); // コンパイルエラー!型が合わない
}
```
* std::format[link /reference/format/format.md]


## 実装例
```cpp
// 書式文字列のチェック
template <class CharT, class... Args>
consteval void fmt_checker(std::basic_string_view<CharT> str)
{
// …

if (/*カッコの開き・閉じが一致しない場合*/) {
throw "invalid brackets"; // throw式は定数式で実行できないため、
// このパスを通ったらコンパイルエラーになる
}

// …

if (/*型が合わない時*/) {
throw "invalid type specifier";
}

// …
}

namespace std {
template <class CharT, class... Args>
struct basic_format_string {
std::basic_string_view<CharT> str;

template <class T>
requires std::convertible_to<const T&, std::basic_string_view<charT>>
consteval basic_format_string(const T& s)
: str(s)
{
fmt_checker<CharT, Args...>(str);
}
};

template <class... Args>
using format_string = basic_format_string<char, std::type_identity_t<Args>...>;
}
```
* std::convertible_to[link /reference/concepts/convertible_to.md]
* std::type_identity_t[link /reference/type_traits/type_identity.md]
## バージョン
### 言語
- C++23
### 処理系
- [Clang](/implementation.md#clang): ??
- [GCC](/implementation.md#gcc): 13.1
- [Visual C++](/implementation.md#visual_cpp): ??
## 参照
- [P2216R3 `std::format` improvements](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2216r3.html)
33 changes: 7 additions & 26 deletions reference/format/format.md
Expand Up @@ -23,8 +23,8 @@ namespace std {
* string[link /reference/string/basic_string.md]
* wstring[link /reference/string/basic_string.md]
* locale[link /reference/locale/locale.md]
* format_string[italic]
* wformat_string[italic]
* format_string[link basic_format_string.md]
* wformat_string[link basic_format_string.md]
## 概要
Expand Down Expand Up @@ -250,32 +250,13 @@ format("{:n}", 1234); // "1,234" (ロケールによる)
### コンパイル時の書式文字列チェック
_`format_string`_ および _`basic_format_string`_ は次のように定義される。ただし、クラス名は規定されない
書式文字列は、[`basic_format_string`](basic_format_string.md)クラスによってコンパイル時にチェックされる
```cpp
namespace std {
template<class charT, class... Args> struct basic_format_string { // exposition only
private:
basic_string_view<charT> str; // exposition only
public:
template<class T> consteval basic_format_string(const T& s): str(s) {
/*何らかのコンパイル時書式文字列チェック*/
}
};
template<class... Args> using format_string
= basic_format_string<char, type_identity_t<Args>...>; // exposition only
template<class... Args> using wformat_string
= basic_format_string<wchar_t, type_identity_t<Args>...>; // exposition only
```
* consteval[link /lang/cpp20/immediate_functions.md]

_`basic_format_string`_ のコンストラクタは[即時関数](/lang/cpp20/immediate_functions.md)であり、書式文字列が正しい場合にのみ定数式として評価できるように実装される。即時関数であるため、定数式として評価できない場合はエラーとなる。
[`basic_format_string`](basic_format_string.md) のコンストラクタは[即時関数](/lang/cpp20/immediate_functions.md)であり、書式文字列が正しい場合にのみ定数式として評価できるように実装される。即時関数であるため、定数式として評価できない場合はエラーとなる。
書式文字列チェックをエラーがあった場合に例外を投げるような実装をすれば、`throw`は定数式として評価できないため、コンパイルエラーとすることが可能である。
## 適格要件
* 書式文字列は定数式であり、[`string_view`](/reference/string_view/basic_string_view.md)(ワイド文字列版は[`wstring_view`](/reference/string_view/basic_string_view.md))に暗黙変換できること。
Expand Down Expand Up @@ -557,8 +538,8 @@ wstring format(const locale& loc, wformat_string<Args...> fmt, const Args&... ar
```
* string[link /reference/string/basic_string.md]
* wstring[link /reference/string/basic_string.md]
* format_string[italic]
* wformat_string[italic]
* format_string[link basic_format_string.md]
* wformat_string[link basic_format_string.md]
* str[italic]
* vformat[link vformat.md]
* make_format_args[link make_format_args.md]
Expand Down
8 changes: 4 additions & 4 deletions reference/format/format_to.md
Expand Up @@ -20,8 +20,8 @@ namespace std {
Out format_to(Out out, const locale& loc, wformat_string<Args...> fmt, const Args&... args); // (4)
}
```
* format_string[italic]
* wformat_string[italic]
* format_string[link basic_format_string.md]
* wformat_string[link basic_format_string.md]
* locale[link /reference/locale/locale.md]
## 概要
Expand Down Expand Up @@ -135,8 +135,8 @@ wstring format_to(Out out, const locale& loc, wformat_string<Args...> fmt, const
return vformat_to(out, loc, fmt.str, {make_format_args<context>(args...)});
}
```
* format_string[italic]
* wformat_string[italic]
* format_string[link basic_format_string.md]
* wformat_string[link basic_format_string.md]
* str[italic]
* basic_format_context[link basic_format_context.md]
* vformat_to[link vformat.md]
Expand Down

0 comments on commit f770ec5

Please sign in to comment.