C++の応用 - YAML

2024年8月20日 (火) 16:54時点におけるWiki (トーク | 投稿記録)による版 (ページの作成:「== 概要 == YAML (YAML Ain't Markup Language) は、人間にとって読み書きしやすいデータシリアライゼーション形式である。<br> 設定ファイル、データ交換、データ保存等、様々な用途で使用されている。<br> <br> YAMLの特徴を以下に示す。<br> * 可読性が高い *: インデントを使用して構造を表現するため、人間が読みやすい形式である。 * 豊富なデータ型 *: 文字…」)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)

概要

YAML (YAML Ain't Markup Language) は、人間にとって読み書きしやすいデータシリアライゼーション形式である。
設定ファイル、データ交換、データ保存等、様々な用途で使用されている。

YAMLの特徴を以下に示す。

  • 可読性が高い
    インデントを使用して構造を表現するため、人間が読みやすい形式である。
  • 豊富なデータ型
    文字列、数値、ブール値、リスト、マップ (辞書) 等の基本的なデータ型をサポートしている。
  • 柔軟性
    複雑なデータ構造も表現できる。
  • コメントのサポート
    #を使用して、コメントを記述することができる。
  • 参照と別名
    データの再利用や循環参照が可能である。


YAMLの使用するメリットを以下に示す。

  • 設定ファイルに適している
    人間が読み書きしやすいため、アプリケーションの設定ファイルとしてよく使用されている。
  • データ交換に適している
    JSONの代替として使用でき、より読みやすい形式でデータを表現できる。
  • 柔軟性が高い
    複雑なデータ構造も簡単に表現できる。
  • 多くの言語でサポートされている
    多くのプログラミング言語にYAMLパーサーが存在する。


YAMLは、可読性の高さと柔軟性から、多くの開発者に好まれている。
特に、設定ファイルやデータ交換の用途で広く使用されており、多くのモダンなアプリケーションやフレームワークでYAMLが採用されている。

C++では、YAMLの処理によく使用されるライブラリの1つにyaml-cppライブラリが存在する。


YAMLの基本的な構造

※注意

  • インデントが重要
    スペースを使用してインデントを行い、一貫性を保つ必要がある。
  • コロンの後にはスペースが必要
    "<キー>: <値>"のように、コロンの後にスペースを入れる必要がある。
  • 文字列のクォート
    特殊文字を含む場合や曖昧さを避けたい場合は、文字列をクォートで囲むことができる。


<syntaxhighlight lang="yaml">
# This is a comment
---  # Document start

# スカラー値 (文字列, 数値, ブール値)
name: John Doe
age: 30
is_student: false

# リスト (ハイフンを使用)
hobbies:
  - reading
  - traveling
  - photography

# マッピング (キー: 値の形式)
address:
  street: 123 Main St
  city: Anytown
  country: USA

# 複雑なデータ構造 (リストとマッピングの組み合わせ)
employees:
  - name: Alice
    position: Developer
    skills:
      - Python
      - JavaScript
  - name: Bob
    position: Designer
    skills:
      - Photoshop
      - Illustrator

# 複数行の文字列
description: >
  This is a long description
  that spans multiple lines.
  The > symbol preserves newlines
  but removes extra whitespace.

# 複数行の文字列 (> と | を使用)
code_sample: |
  def hello_world():
     print("Hello, World!")

# アンカー ("&") と エイリアス ("*") を使用したデータの再利用
defaults: &defaults
  timeout: 30
  retries: 3

production:
  <<: *defaults
  host: production.example.com
development:
  <<: *defaults
  host: dev.example.com
  timeout: 10  # デフォルト値のオーバーライド

# Document end
</syntaxhighlight>



yaml-cppライブラリ

yaml-cppライブラリとは

yaml-cppライブラリは、C++向けのYAMLパーサーおよびエミッタライブラリである。

yaml-cppライブラリを使用することにより、C++でYAMLファイルの読み込み、操作、書き込みが可能になる。
また、設定ファイルの処理、データのシリアライズ / デシリアライズ、構造化データの操作等に広く使用されている。

yaml-cppライブラリの特徴を以下に示す。

  • C++ 11以降をサポート
    モダンなC++の機能を活用している。
  • ヘッダオンリーライブラリ
    使用する場合は、ヘッダファイルをインクルードするだけで済む。
  • YAML 1.2仕様のサポート
    最新のYAML仕様に準拠している。
  • 例外ベースのエラー処理
    エラーが発生した場合は、適切な例外をスローする。
  • STLとの統合
    STLコンテナとの相互運用性が高い。
  • ストリーミングAPI
    大きなファイルや継続的なデータストリームの処理に適している。


yaml-cppライブラリのライセンス

yaml-cppライブラリのライセンスは、MITライセンスに準拠している。

yaml-cppの主要なクラス

  • YAML::Nodeクラス
    YAMLデータを表現する基本的なクラスである。
    スカラー、シーケンス、マップ等、あらゆる種類のYAMLノードを表現できる。
  • YAML::Exceptionクラス
    yaml-cppライブラリが投げる例外の基本クラスである。
    パース時のエラーや型変換エラー等をキャッチできる。
  • YAML::Emitterライブラリ
    YAMLデータを生成するためのクラスである。
    プログラム上において、YAMLを構築する時に使用する。
  • YAML::Parserクラス
    低レベルのパーシング操作を行うためのクラスである。
    一般的には、YAML::LoadクラスやYAML::LoadFileクラスを使用するため、直接使用することは少ない。


※注意

  • 型安全性
    YAML::Nodeクラスは、テンプレートベースの.as<T>メソッドを提供するが、型が一致しない場合は例外がスローされる。
    そのため、適切な型チェックを行うことが重要である。
  • メモリ管理
    YAML::Nodeクラスのオブジェクトは参照カウント方式で管理されているため、通常はメモリリークを心配する必要は無い。
  • パフォーマンス
    大規模なファイルを扱う場合、YAML::LoadFileクラスはファイル全体をメモリに読み込むため、メモリ使用量が増大する可能性がある。
    この場合、ストリーミングAPIの使用を検討すること。
  • エラー処理
    yaml-cppライブラリは例外を使用してエラーを報告する。
    そのため、適切なtry-catchブロックを使用して、エラーをハンドリングすることが重要である。


yaml-cppライブラリのインストール

パッケージ管理システムからインストール
# RHEL
sudo dnf install yaml-cpp yaml-cpp-devel

# SUSE
sudo zypper install yaml-cpp yaml-cpp-devel


ソースコードからインストール

yaml-cppライブラリのGithubにアクセスして、ソースコードをダウンロードする。
ダウンロードしたファイルを解凍する。

tar xf yaml-cpp-<バージョン>.tar.gz
cd yaml-cpp-<バージョン>


yaml-cppライブラリをビルドおよびインストールする。

mkdir build && cd build

cmake -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_INSTALL_PREFIX=<yaml-cppライブラリのインストールディレクトリ> \
      -DYAML_BUILD_SHARED_LIBS=ON \
      ..
make -j $(nproc)
make install



ライブラリの指定

Qtプロジェクトファイルを使用する場合

<syntaxhighlight lang="make">
# Qtプロジェクトファイル

# pkg-configを使用してyaml-cppライブラリを設定
CONFIG += link_pkgconfig
PKGCONFIG += yaml-cpp

# pkg-configを使用しない場合
LIBS += -lyaml-cpp
</syntaxhighlight>


CMakeLists.txtファイルを使用する場合

<syntaxhighlight lang="cmake">
# CMakeLists.txtファイル

# ...略

find_package(PkgConfig REQUIRED)

# yaml-cppライブラリの指定
pkg_check_modules(YAML_CPP REQUIRED yaml-cpp)

# インクルードディレクトリの指定
target_include_directories(${PROJECT_NAME} PRIVATE
  # ...略
  ${YAML_CPP_INCLUDE_DIRS}
)

# ...略

# ライブラリのリンク
target_link_libraries(${PROJECT_NAME} PRIVATE
   # ...略
   ${YAML_CPP_LIBRARIES}
)

# コンパイルオプションの設定
target_compile_options(${PROJECT_NAME} PRIVATE
   # ...略
  ${YAML_CPP_CFLAGS_OTHER}
)
</syntaxhighlight>



YAMLファイルの読み込み

以下の例では、yaml-cppライブラリの主な機能を使用している。

  • YAMLファイルの読み込み (YAML::LoadFileクラスの使用)
  • スカラー値の取得 (.as<T>メソッドの使用)
  • リストの処理 (範囲ベースのforループ)
  • ネストされたマップの処理
  • 新しいYAMLノードの作成と操作
  • YAMLデータの出力 (ストリーム演算子<<の使用)
  • YAMLデータのファイルへの書き込み


<syntaxhighlight lang="c++">
// main.cppファイル

#include <iostream>
#include <yaml-cpp/yaml.h>

int main()
{
   try {
      // YAMLファイルの読み込み
      YAML::Node config = YAML::LoadFile("config.yaml");

      // スカラー値の取得
      std::string name = config["name"].as<std::string>();
      int age = config["age"].as<int>();
      std::cout << "Name: " << name << ", Age: " << age << std::endl;

      // リストの処理
      std::cout << "Hobbies:" << std::endl;
      for (const auto &hobby : config["hobbies"]) {
         std::cout << "- " << hobby.as<std::string>() << std::endl;
      }

      // ネストされたマップの処理
      if (config["address"]) {
         std::cout << "Address:" << std::endl;
         std::cout << "  Street: " << config["address"]["street"].as<std::string>() << std::endl;
         std::cout << "  City: " << config["address"]["city"].as<std::string>() << std::endl;
      }

      // 新しいYAMLノードの作成
      YAML::Node newNode;
      newNode["key"] = "value";
      newNode["list"].push_back(1);
      newNode["list"].push_back(2);
      newNode["list"].push_back(3);

      // YAMLの出力
      std::cout << "\nNew YAML:\n" << newNode << std::endl;

      // TAMLファイルへの書き込み
      std::ofstream fout("output.yaml");
      fout << newNode;
      fout.close();
   }
   catch (const YAML::Exception& e) {
      std::cerr << "YAML error: " << e.what() << std::endl;
      return -1;
   }

   return 0;
}
</syntaxhighlight>


yaml-cppライブラリの詳細な使用方法は、公式ドキュメントを参照すること。

YAMLを出力するモデルは、std::ostreamマニピュレータである。
YAML::Emitterクラスのオブジェクトは出力ストリームとして動作して、その出力はc_strメソッドで取得できる。
YAML::Emitterクラスの詳細な使用方法は、公式ドキュメントを参照すること。