# 帳票設計書 11-print / puts 出力

## 概要

本ドキュメントは、mrubyにおけるKernel#print / Kernel#putsメソッドによる標準出力への文字列出力機能の設計仕様を記述する。IO gemの有無により実装が切り替わる二重構造を持つ帳票出力機能である。

### 本帳票の処理概要

mrubyのKernel#printおよびKernel#putsメソッドは、Rubyオブジェクトを文字列に変換して標準出力(stdout)に出力する機能を提供する。

**業務上の目的・背景**：mrubyはCRubyと同様にRuby言語仕様に準拠した出力メソッドを提供する必要がある。printとputsはRubyプログラムにおける最も基本的な出力手段であり、ISO規格(ISO 15.3.1.3.35 Kernel#print)にも定義されている。組み込み環境でも動作する軽量実装(src/print.c)と、フル機能のIO gem実装(mrbgems/mruby-io)の二つの実装を用意することで、リソース制約の異なる環境に対応している。

**帳票の利用シーン**：mrubyスクリプトの実行時に、ユーザがprint/putsメソッドを呼び出してコンソールやターミナルへ文字列を出力する場面で使用される。デバッグ出力、ログ出力、ユーザインタラクション等の幅広い場面で利用される。

**主要な出力内容**：
1. 引数として渡されたRubyオブジェクトのto_s変換結果の文字列
2. putsの場合は各引数の末尾に改行文字を自動付加
3. printの場合は改行なしで引数を連結出力

**帳票の出力タイミング**：mrubyスクリプト内でprint/putsメソッドが呼び出された時点で即座に標準出力へ出力される。端末(tty)接続時はfflushによる即時フラッシュが行われる。

**帳票の利用者**：mrubyスクリプトの開発者、mruby組み込みアプリケーションのエンドユーザ

## 帳票種別

標準出力(テキスト出力)

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | コンソール/ターミナル | N/A (標準出力) | Rubyスクリプト内でprint/putsメソッドを呼び出し |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | テキスト(標準出力ストリーム) |
| 用紙サイズ | N/A |
| 向き | N/A |
| ファイル名 | N/A (stdoutへの直接出力) |
| 出力方法 | 標準出力(stdout)への書き込み |
| 文字コード | UTF-8 (Windows環境ではUTF-16への変換あり) |

### PDF固有設定

N/A (本帳票はPDF出力ではない)

### Excel固有設定

N/A (本帳票はExcel出力ではない)

## 帳票レイアウト

### レイアウト概要

print/putsは可変長のテキスト出力であり、固定レイアウトは持たない。引数として渡されたオブジェクトのto_s変換結果がそのまま出力される。

```
┌─────────────────────────────────────┐
│ [引数1のto_s結果][引数2のto_s結果]...│  ← print の場合
├─────────────────────────────────────┤
│ [引数1のto_s結果]\n                  │  ← puts の場合
│ [引数2のto_s結果]\n                  │
│ ...                                 │
└─────────────────────────────────────┘
```

### ヘッダー部

N/A (ヘッダーなし)

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | 出力文字列 | 引数オブジェクトのto_s変換結果 | Rubyメソッド引数(argv) | 文字列そのまま | 可変 |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | 改行文字 | putsの場合のみ末尾に"\n"を付加 | 固定値 | "\n" |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| MRB_NO_STDIO未定義 | MRB_NO_STDIOが定義されている場合、出力は何も行わない(空実装) | Yes |
| HAVE_MRUBY_IO_GEM | mruby-io gemがビルドに含まれる場合はIO gem側の実装が使用される | No |

### ソート順

N/A (順次出力のためソート不要)

### 改ページ条件

N/A (標準出力のため改ページなし)

## データベース参照仕様

### 参照テーブル一覧

N/A (データベースを参照しない)

### テーブル別参照項目詳細

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| 出力文字列 | mrb_obj_as_string(mrb, argv[i]) | N/A | 非String型オブジェクトはto_sメソッドで変換 |
| Windows UTF-16変換 | MultiByteToWideChar(CP_UTF8, ...) | N/A | Windows環境でtty接続時のみUTF-16に変換して出力 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[print/puts メソッド呼び出し] --> B{HAVE_MRUBY_IO_GEM?}
    B -->|定義済| C[IO gem側 io_print/io_puts]
    B -->|未定義| D[src/print.c mrb_print_m]
    C --> E[io_get_write_fptr でFDを取得]
    E --> F[fd_write で直接write]
    D --> G[mrb_obj_as_string で文字列変換]
    G --> H[printstr 関数呼び出し]
    H --> I{Windows環境?}
    I -->|Yes| J{tty接続?}
    J -->|Yes| K[UTF-16変換 → WriteConsoleW]
    J -->|No| L[fwrite で出力]
    I -->|No| L
    K --> M{tty接続?}
    L --> M
    M -->|Yes| N[fflush stdout]
    M -->|No| O[終了]
    N --> O
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| MRB_NO_STDIO | MRB_NO_STDIOマクロが定義されている場合 | (出力なし - 空実装) | ビルド設定を変更してMRB_NO_STDIOを無効にする |
| write失敗 | IO gem使用時にwrite()システムコールが失敗 | "syswrite" (mrb_sys_fail) | ファイルディスクリプタの状態を確認 |
| メモリ不足 | Windows UTF-16変換時のmrb_mallocが失敗 | (例外発生) | メモリを確保する |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 制限なし(引数の個数に制限なし) |
| 目標出力時間 | 即時(システムコール依存) |
| 同時出力数上限 | N/A (シングルスレッド実行) |

## セキュリティ考慮事項

- 標準出力への出力であるため、出力内容にアクセス制御は適用されない
- Windows環境ではUTF-8からUTF-16への変換が行われるため、不正なUTF-8シーケンスによる変換エラーの可能性がある
- tty接続判定(isatty)によりフラッシュの有無が切り替わるが、セキュリティ上の問題は限定的

## 備考

- mruby-io gemが存在する場合、Kernel#print/Kernel#putsの実装はIO gem側(io_print/io_puts)に切り替わる。これはmrbgems/mruby-io/mrbgem.rakeで`HAVE_MRUBY_IO_GEM`マクロが定義されることで実現される
- IO gem版ではputsがArray引数を再帰的に展開する機能を持つ(io_puts_ary関数)
- printはsrc/print.cの軽量版ではKernel#printのみが提供され、putsはIO gemが必要

---

## コードリーディングガイド

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

mruby値型(mrb_value)と文字列型(mrb_string)の基本構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | mruby.h | `include/mruby.h` | mrb_value型の定義、MRB_API宣言 |
| 1-2 | string.h | `include/mruby/string.h` | RSTRING_PTR, RSTRING_LENマクロによる文字列データアクセス |

**読解のコツ**: mrb_valueは全てのRubyオブジェクトを表現するC構造体であり、RSTRING系マクロで内部の文字列バッファにアクセスする。

#### Step 2: エントリーポイントを理解する

Kernel#printメソッドのC実装がどこで定義・登録されているかを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | kernel.c | `src/kernel.c` | Kernel#print, Kernel#pのメソッド登録(行697-699)、mrb_p_m関数(行652-665) |
| 2-2 | print.c | `src/print.c` | mrb_print_m関数(行70-81)、printstr/printcstr内部関数 |

**主要処理フロー**:
1. **行697-699 (kernel.c)**: HAVE_MRUBY_IO_GEM未定義時にKernel#p, Kernel#printが登録される
2. **行70-81 (print.c)**: mrb_print_m関数 - 可変長引数を受け取り、各引数をprintstrで出力
3. **行46-52 (print.c)**: printstr関数 - mrb_value を文字列に変換してprintcstrに渡す
4. **行26-43 (print.c)**: printcstr関数 - 実際のバイト列出力(Windows分岐あり)

#### Step 3: IO gem側の実装を理解する

HAVE_MRUBY_IO_GEMが定義されている場合の代替実装を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | mrbgem.rake | `mrbgems/mruby-io/mrbgem.rake` | HAVE_MRUBY_IO_GEMマクロの定義(行6) |
| 3-2 | io.c | `mrbgems/mruby-io/src/io.c` | io_print関数(行1062-1082)、io_puts関数(行1020-1052)、io_write関数(行954-976) |
| 3-3 | io.c | `mrbgems/mruby-io/src/io.c` | メソッド登録テーブル(行2228-2231) |

**主要処理フロー**:
- **行954-976 (io.c)**: io_write - fd_writeを使ってファイルディスクリプタに直接書き込み
- **行1062-1082 (io.c)**: io_print - 各引数をfd_writeで出力(改行なし)
- **行1020-1052 (io.c)**: io_puts - 各引数をio_puts_strで出力(改行自動付加)
- **行978-994 (io.c)**: io_puts_str - 文字列末尾に改行がなければ追加

### プログラム呼び出し階層図

```
Kernel#print (Ruby側)
    |
    +-- [HAVE_MRUBY_IO_GEM未定義時]
    |   |
    |   +-- mrb_print_m (src/print.c:70)
    |          +-- mrb_obj_as_string (to_s変換)
    |          +-- printstr (src/print.c:46)
    |                 +-- printcstr (src/print.c:26)
    |                        +-- [Windows tty] MultiByteToWideChar → WriteConsoleW
    |                        +-- [その他] fwrite
    |
    +-- [HAVE_MRUBY_IO_GEM定義時]
        |
        +-- io_print (mrbgems/mruby-io/src/io.c:1062)
               +-- io_get_write_fptr
               +-- io_prepare_write
               +-- mrb_obj_as_string
               +-- fd_write (io.c:915)
                      +-- write() システムコール

Kernel#puts (Ruby側) ※IO gem必要
    |
    +-- io_puts (mrbgems/mruby-io/src/io.c:1020)
           +-- io_get_write_fptr
           +-- io_prepare_write
           +-- io_puts_str (io.c:980)
           |      +-- fd_write
           |      +-- [末尾改行なしの場合] fd_write("\n")
           +-- io_puts_ary (io.c:998) ※Array引数時
                  +-- io_puts_str (再帰)
```

### データフロー図

```
[入力]                    [処理]                          [出力]

Ruby引数         mrb_obj_as_string()
(mrb_value) ───> (to_s変換) ───> printcstr/fd_write ───> stdout
                                      |
                                      +── [Win tty] UTF-16変換
                                      +── [puts] 改行付加
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| print.c | `src/print.c` | ソース | Kernel#printの軽量実装(IO gem不在時) |
| kernel.c | `src/kernel.c` | ソース | Kernel#print/Kernel#pのメソッド登録 |
| io.c | `mrbgems/mruby-io/src/io.c` | ソース | IO gem版のprint/puts/write実装 |
| mruby_io_gem.c | `mrbgems/mruby-io/src/mruby_io_gem.c` | ソース | IO gem初期化エントリーポイント |
| mrbgem.rake | `mrbgems/mruby-io/mrbgem.rake` | 設定 | HAVE_MRUBY_IO_GEMマクロ定義 |
| string.h | `include/mruby/string.h` | ヘッダ | RSTRING_PTR/RSTRING_LENマクロ |
| mruby.h | `include/mruby.h` | ヘッダ | mrb_value型定義、MRB_API宣言 |
