From d20bf1b0fcd19416d92c634a1af949944e44a568 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Wed, 3 Dec 2025 10:28:57 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix(mblen):=20=E8=AB=B8=E3=80=85=E3=81=AE?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * std::mblen の内部状態をクリアせずに使用しているのを修正 * size_t に対する std::size_t のつけ忘れを修正 * nullptr を指定した時の戻り値の記述を修正 * 関連するロカールカテゴリを明記 --- reference/cstdlib/mblen.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/reference/cstdlib/mblen.md b/reference/cstdlib/mblen.md index 4cdcd924d0..e205197371 100644 --- a/reference/cstdlib/mblen.md +++ b/reference/cstdlib/mblen.md @@ -14,13 +14,13 @@ namespace std { 先頭以外の文字に関するバイト数は計算されない。 -この関数は現在のロケールに依存してマルチバイト文字を解釈する。 +この関数は現在のロケールカテゴリー `LC_CTYPE` に依存してマルチバイト文字を解釈する。 `n`は解析に使用する最大バイト数を指定する。 ## 戻り値 - 正常に動作する場合、文字の占めるバイト数を返す。 -- `str`が`nullptr`の時、内部状態を初期化し`0`を返す。 +- `str`が`nullptr`の時、内部状態を初期化する。現在のエンコーディングが状態を持つ場合は非ゼロの値を返し、それ以外の場合は`0`を返す。 - 無効な文字列、または`n`が不足している場合、`-1`を返す。 ## 例 @@ -51,8 +51,11 @@ int main() { #include int count_chars_mblen(const char* s) { + // std::mblen 内部の std::mbstate_t を初期化する必要あり + std::mblen(nullptr, 0); + int count = 0; - size_t i = 0; + std::size_t i = 0; while (s[i] != '\0') { int len = std::mblen(&s[i], MB_CUR_MAX); if (len < 0) { From e709dd2f2f33ff6510a95175ae8360e207d74cf5 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Wed, 3 Dec 2025 11:01:35 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix(mblen):=20=E3=83=90=E3=83=83=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=AA=E3=83=BC=E3=83=90=E3=83=BC=E3=83=A9=E3=83=B3?= =?UTF-8?q?=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 残り MB_CUR_MAX バイトが読み取れることが保証されていないと未定義動作 Co-authored-by: Raclamusi --- reference/cstdlib/mblen.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/reference/cstdlib/mblen.md b/reference/cstdlib/mblen.md index e205197371..4bdf01934d 100644 --- a/reference/cstdlib/mblen.md +++ b/reference/cstdlib/mblen.md @@ -26,14 +26,15 @@ namespace std { ## 例 ### 基本的な使い方 ```cpp example -#include -#include #include +#include +#include +#include int main() { std::setlocale(LC_ALL, "ja_JP.UTF-8"); const char *str = "こんにちは"; - int result = std::mblen(str, MB_CUR_MAX); + int result = std::mblen(str, std::strlen(str)); std::cout << result << std::endl; return 0; } @@ -46,9 +47,10 @@ int main() { ### 文字列の文字数を計算する ```cpp example -#include -#include #include +#include +#include +#include int count_chars_mblen(const char* s) { // std::mblen 内部の std::mbstate_t を初期化する必要あり @@ -56,8 +58,9 @@ int count_chars_mblen(const char* s) { int count = 0; std::size_t i = 0; - while (s[i] != '\0') { - int len = std::mblen(&s[i], MB_CUR_MAX); + std::size_t bytes = std::strlen(s); + while (i < bytes) { + int len = std::mblen(&s[i], bytes - i); if (len < 0) { len = 1; } From bd71f1a96f73d1d54eaf6c57bac3680189d5b7de Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Wed, 3 Dec 2025 10:48:37 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat(mblen):=20=E5=86=85=E9=83=A8=E7=8A=B6?= =?UTF-8?q?=E6=85=8B=E3=81=A8=20std::mbrlen=20=E3=82=92=E8=A8=80=E5=8F=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reference/cstdlib/mblen.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/reference/cstdlib/mblen.md b/reference/cstdlib/mblen.md index 4bdf01934d..c38e3d3d84 100644 --- a/reference/cstdlib/mblen.md +++ b/reference/cstdlib/mblen.md @@ -18,6 +18,11 @@ namespace std { `n`は解析に使用する最大バイト数を指定する。 +この関数は `std::mbstate_t` に等価な静的記憶域の内部状態を保持し、前回の `mblen` 関数呼び出しの続きとして処理を行う。 +従って、この関数はスレッドセーフではない。 +スレッドセーフに処理する場合は、`std::mbstate_t` を受け取る [`std::mbrlen`](../cwchar/mbrlen.md.nolink) (``) を使い、呼び出し元でデコード状態の記録場所 `std::mbstate_t` を用意する必要がある。 +新しいコードでは、特に理由がない限り `std::mblen` ではなく `std::mbrlen` を用いるべきである。 + ## 戻り値 - 正常に動作する場合、文字の占めるバイト数を返す。 - `str`が`nullptr`の時、内部状態を初期化する。現在のエンコーディングが状態を持つ場合は非ゼロの値を返し、それ以外の場合は`0`を返す。 @@ -79,8 +84,15 @@ int main() { } ``` +注意: この例は飽くまで `std::mblen` を用いて文字数を数える関数の例であるが、実用上は `std::mbrlen` を用いた実装にするのが安全である。 +上の関数 `count_chars_mblen` はスレッドーセーフでない他、呼び出し元でも `std::mblen` を使っている場合にその振る舞いを破壊する可能性がある。 + #### 出力例 ``` 文字列: こんにちは世界 文字数: 7 ``` + +## 関連項目 + +- [`mbrlen`](../cwchar/mbrlen.md.nolink): `std::mbstate_t` を受け取るスレッドセーフなバージョン From 8489a5dfc901279ef50211262abb29f46d00e595 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Tue, 6 Jan 2026 14:35:23 +0900 Subject: [PATCH 4/5] =?UTF-8?q?feat(mblen):=20C=E8=A6=8F=E6=A0=BC=E3=81=AE?= =?UTF-8?q?=E5=86=85=E9=83=A8=E7=8A=B6=E6=85=8B=E3=81=AB=E9=96=A2=E3=81=99?= =?UTF-8?q?=E3=82=8B=E5=8F=96=E3=82=8A=E6=89=B1=E3=81=84=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=E3=81=AB=E3=81=A4=E3=81=84=E3=81=A6=E8=A8=98=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reference/cstdlib/mblen.md | 47 ++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/reference/cstdlib/mblen.md b/reference/cstdlib/mblen.md index c38e3d3d84..afb248e4b4 100644 --- a/reference/cstdlib/mblen.md +++ b/reference/cstdlib/mblen.md @@ -18,10 +18,42 @@ namespace std { `n`は解析に使用する最大バイト数を指定する。 -この関数は `std::mbstate_t` に等価な静的記憶域の内部状態を保持し、前回の `mblen` 関数呼び出しの続きとして処理を行う。 +エンコーディングの内部状態に関する振る舞いが最近の規格で変更されたことから、 +内部状態に依存するエンコーディングを考慮するならば、 +特に理由がない限り `std::mblen` ではなく `std::mbstate_t` を受け取る `std::mbrlen` を +新しいコードでは用いるべきである。 +以前の振る舞いではスレッドセーフではなく、 +現在の振る舞いではそもそも内部状態に依存する使い方ができない。 + +### C11, C++17, POSIX.1-2024 以降 + +この関数は以下の関数呼び出しに等価である。 + +```cpp +mbtowc((wchar_t *)0, (const char *)0, 0); +mbtowc((wchar_t *)0, str, n); +``` +* mbtowc[link mbtowc.md.nolink] + +つまり、関数 `mblen` はあたかも内部状態がないかのように振る舞い、 +現在のエンコーディングの初期状態を用いて `str` の先頭にある文字のバイト数を計算する。 + +### C99, C++14, POSIX.1-2017 以前 + +この関数は以下の関数呼び出しに等価である。 + +```cpp +mbtowc((wchar_t *)0, str, n); +``` +* mbtowc[link mbtowc.md.nolink] + +関数 `mblen` は `std::mbstate_t` に等価な静的記憶域の内部状態を保持し、前回の `mblen` 関数呼び出しの続きとして処理を行う。 従って、この関数はスレッドセーフではない。 -スレッドセーフに処理する場合は、`std::mbstate_t` を受け取る [`std::mbrlen`](../cwchar/mbrlen.md.nolink) (``) を使い、呼び出し元でデコード状態の記録場所 `std::mbstate_t` を用意する必要がある。 -新しいコードでは、特に理由がない限り `std::mblen` ではなく `std::mbrlen` を用いるべきである。 +また、この関数を用いる処理の途中で、この関数を用いる別の処理を行うこともできない。 +安全に処理するためには、この関数の代わりに `std::mbstate_t` を受け取る [`std::mbrlen`](../cwchar/mbrlen.md.nolink) (``) を使い、呼び出し元でデコード状態の記録場所 `std::mbstate_t` を用意する必要がある。 + +他の標準ライブラリ関数はあたかも`mblen`を呼び出さないように振る舞う。 +つまり、明示的に `mblen` を呼び出さない限り、その内部状態を変更しない。 ## 戻り値 - 正常に動作する場合、文字の占めるバイト数を返す。 @@ -58,8 +90,15 @@ int main() { #include int count_chars_mblen(const char* s) { - // std::mblen 内部の std::mbstate_t を初期化する必要あり +#if __cplusplus >= 201703L + if (std::mblen(nullptr, 0) != 0) { + std::cerr << "count_chars_mblen: State-dependent encoding is unsupported." << std::endl; + std::exit(1); + } +#else + // C++14 以前: std::mblen 内部の std::mbstate_t を初期化する必要あり std::mblen(nullptr, 0); +#endif int count = 0; std::size_t i = 0; From 2c1699c50258564e002b7f4ca4baa58307b85d49 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Tue, 6 Jan 2026 15:37:48 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feat(mblen):=20C=E8=A6=8F=E6=A0=BC=E5=A4=89?= =?UTF-8?q?=E6=9B=B4=E3=81=AE=E7=B5=8C=E7=B7=AF=E3=81=AB=E3=81=A4=E3=81=84?= =?UTF-8?q?=E3=81=A6=E3=81=AE=E9=96=A2=E9=80=A3=E9=A0=85=E7=9B=AE=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reference/cstdlib/mblen.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/reference/cstdlib/mblen.md b/reference/cstdlib/mblen.md index afb248e4b4..dc6fa62bae 100644 --- a/reference/cstdlib/mblen.md +++ b/reference/cstdlib/mblen.md @@ -18,10 +18,10 @@ namespace std { `n`は解析に使用する最大バイト数を指定する。 -エンコーディングの内部状態に関する振る舞いが最近の規格で変更されたことから、 内部状態に依存するエンコーディングを考慮するならば、 特に理由がない限り `std::mblen` ではなく `std::mbstate_t` を受け取る `std::mbrlen` を 新しいコードでは用いるべきである。 +エンコーディングの内部状態に関する振る舞いが C11 で変更 (WG14 N1373) されたが、 以前の振る舞いではスレッドセーフではなく、 現在の振る舞いではそもそも内部状態に依存する使い方ができない。 @@ -135,3 +135,14 @@ int main() { ## 関連項目 - [`mbrlen`](../cwchar/mbrlen.md.nolink): `std::mbstate_t` を受け取るスレッドセーフなバージョン +- [WG14 N1373: Wording improvements for mblen, mbtowc, and c16rtomb](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1373.htm) + - C11 以降内部状態が最初に初期化されることになった。「表現の改善」としてこの破壊的な変更が実施されたように見える。 + - [musl のメーリングリスト - 2019-12-26](https://www.openwall.com/lists/musl/2019/12/26/7) にて Florian Weimer が以下のように指摘している: + "actual users of the interfaces with legacy charsets do not seem to be represented on the standards committee anymore (see the mblen behavioral change in C11 as evidence supporting this theory)." + 意訳: 「(C11 で入った mblen の振る舞い変更でも分かるように) C標準化委員会には古い文字コードを使っている人はもういないようだ。」 +- [WG14 N2037: mblen, mbtowc, and wctomb thread-safety](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2037.htm) + - [WG14 DR498: mblen, mbtowc, and wctomb thread-safety](https://www.open-std.org/jtc1/sc22/wg14/issues/c11c17/issue0498.html) + - [WG14 N2246: Make mblen, mbtowc, and wctomb thread-safe for encodings that are not state-dependent](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2246.htm) + - [WG14 N2281: Make mblen, mbtowc, and wctomb thread-safe for encodings that are not state-dependent](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2281.htm) + - [WG14 N2358: No internal state for mblen](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2358.htm) + - C11 以降もあたかも `mblen` が内部状態を持つかのような記述になっていたのが C23 で修正された。