Qtの基礎 - TOML
概要
TOML (Tom's Obvious, Minimal Language) は、設定ファイルフォーマットとして設計された。
QtでTOMLを使用することは、アプリケーションの設定や構成を管理する上で有効である。
TOMLの特徴として、人間が読み書きしやすい形式であることが挙げられる。
キーと値のペアを基本構造とし、階層的なデータ構造も表現することができる。
日付や時刻、真偽値、数値等、様々なデータ型をサポートしている。
Qtには組み込みのTOMLパーサーが存在しないため、サードパーティ製ライブラリを使用する必要がある。
一般的に、toml++ライブラリやtoml11ライブラリ等のサードパーティ製ライブラリがよく使用されている。
これらのライブラリを使用することにより、TOMLファイルの読み込み、書き込み、データの解析が容易になる。
TOMLの構文は、基本的な形式は<キー> = <値>
のペアで記述する。
セクションを使用してデータをグループ化することもでき、これはブラケット[]
で囲むことで表現する。
TOMLを使用するメリットとして、設定の管理が容易になることが挙げられる。
例えば、アプリケーションの起動時にTOMLファイルから設定を読み込み、実行時に変更された設定をTOMLファイルに書き込むような使用方法が考えられる。
TOMLは他の設定ファイル形式 (JSONやYAML等) と比較して、より直感的で読みやすい構文を持っている。
特に複雑な階層構造を持つ設定データを扱う場合に、そのメリットが発揮される。
ただし、TOMLを使用する場合は、選択したライブラリの依存関係管理やパフォーマンスへの影響を考慮する必要がある。
TOMLの構文
TOMLファイルの主な構文要素を以下に示す。
- キーと値のペア
- 基本的な形式は、
<キー名> = <値>
である。 - 例:
name = "TOML Example"
- 基本的な形式は、
- 文字列
- 基本文字列
- ダブルクォートで囲む
"Hello"
- リテラル文字列
- シングルクォートで囲む
'C:\Users\username'
- 基本文字列
- 数値
- 整数
42
- 浮動小数点
3.14
- 整数
- ブーリアン
- true または false
- 日付と時刻
- ISO 8601形式を使用する。
- 例:
date = 2023-03-27T15:32:00Z
- 配列
- 角括弧で囲む。
- 例:
colors = [ "red", "yellow", "green" ]
- テーブル (セクション)
- 角括弧で囲んだ名前で定義する。
- 例:
[database] server = "192.168.1.1" ports = [ 8001, 8001, 8002 ]
- インラインテーブル
- 中括弧で囲む。
- 例:
point = { x = 1, y = 2 }
- テーブルの配列
- 2重の角括弧で定義する。
- 例:
[[fruits]] name = "apple" [[fruits]] name = "banana"
- コメント
- シャープ記号 (
#
) を使用する。
- シャープ記号 (
TOMLファイルの例
# config.tomlファイル
title = "設定ファイル" # トップレベルのキー
[user] # ユーザ情報のセクション
name = "山田太郎"
age = 30
email = "yamada@example.com"
[application] # アプリケーション設定のセクション
version = "1.0.0"
debug_mode = false
[database] # データベース接続情報のセクション
host = "localhost"
port = 5432
username = "admin"
password = "secret"
[features] # 機能のオン / オフを制御するセクション
enabled = ["login", "logout", "dashboard"]
disabled = ["admin_panel"]
[logging] # ロギング設定のセクション
level = "info"
file = "/var/log/app.log"
[[servers]] # サーバ情報の配列
ip = "192.168.1.1"
role = "frontend"
[[servers]] # サーバ情報の配列
ip = "192.168.1.2"
role = "backend"
toml++ライブラリ
toml++ライブラリとは
toml++ライブラリは、C++ 17以降で記述されたヘッダファイルのみのTOMパーサーおよびシリアライザーライブラリである。
このライブラリは、TOML v1.0.0仕様に完全準拠しており、使いやすい設計が特徴である。
主な特徴として、テンプレートベースの設計が挙げられる。
これにより、コンパイル時の型チェックが可能となり、実行時のパフォーマンスが向上する。
また、例外を使用しないエラーハンドリングをサポートしており、例外が禁止されている環境でも問題なく使用できる。
toml++は、UTF-8エンコーディングを完全にサポートしており、国際化されたアプリケーションでの使用が容易になる。
さらに、コメントの保持機能も備えており、TOMLファイルを読み込んで修正した後、元のコメントを保持したまま書き出すことができる。
toml++ライブラリは、単一ヘッダファイル版と複数ヘッダファイル版の両方が提供されており、プロジェクトの要件に応じて適切なものを選択することができる。
toml++ライブラリの使用例としては、設定ファイルの読み書き、データのシリアライズ / デシリアライズ、構造化されたデータの保存等が挙げられる。
C++の標準ライブラリと密接に統合されているため、std::stringクラスやその他のSTLコンテナとの相互運用性が高い。
パフォーマンス面では、toml++ライブラリは他の多くのTOMLパーサーよりも高速であることが報告されている。
大規模なTOMLファイルを扱う場合や、頻繁なパース / シリアライズ操作が必要なアプリケーションで特に威力を発揮する。
ドキュメンテーションも充実しており、GitHubリポジトリには詳細な使用方法や設定オプションが記載されている。
また、豊富な例やチュートリアルも提供されているため、初めて使用する開発者でも迅速に習得することができる。
toml++ライブラリのライセンス
toml++ライブラリのライセンスはMITライセンスに準拠しているため、商用プロジェクトを含む幅広い用途で自由に使用することができる。
toml++の主要なクラス
toml++ライブラリの主要クラスを以下に示す。
- toml::table
- TOMLのキー・値のペアを表現する中心的なクラスである。
- std::mapに似た動作をしており、TOMLドキュメント全体や入れ子になったテーブルの表現に使用する。
- toml::array
- TOMLの配列を表現するクラスであり、std::vectorに類似している。
- 異なる型の要素を含むことができる柔軟な配列である。
- toml::value
- TOMLの任意の値 (文字列、整数、浮動小数点数、ブーリアン、日付 / 時刻、配列、テーブル) を表現できる汎用的なクラスである。
- 型安全な方法で値を保持して、必要に応じて型変換を行う。
- toml::date
- toml::time
- toml::date_time
- これらのクラスは日付と時刻の処理に使用する。
- TOMLの日付・時刻形式を精密に表現して、操作するためのものである。
- toml::parse関数とtoml::formatterクラス
- パースとシリアライズの機能を提供する。
- toml::parseは、文字列やストリームからTOMLデータを読み込み、toml::formatterは、TOMLデータを文字列や出力ストリームに書き出す。
- toml::parse_result
- エラーハンドリングに使用されるクラスである。
- パース操作の結果を表し、成功時にはパースされたデータを、失敗時にはエラー情報を提供する。
- toml::node
- TOMLドキュメントの任意のノード (キー、値、テーブル、配列) を表現する抽象基底クラスである。
- 型に依存しない汎用的な操作が可能になる。
toml++ライブラリのインストール
パッケージ管理システムからインストール
# RHEL sudo dnf install tomlplusplus tomlplusplus-devel # SUSE -
ソースコードからインストール
toml++ライブラリのGithubにアクセスして、ソースコードをダウンロードする。
ダウンロードしたファイルを解凍する。
tar xf tomlplusplus-<バージョン>.tar.gz cd tomlplusplus-<バージョン>
toml++ライブラリをビルドおよびインストールする。
mkdir build && cd build # CMakeコマンドを使用する場合 cmake -DCMAKE_CXX_COMPILER=<G++ 8以降のG++> \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=<toml++ライブラリのインストールディレクトリ> \ -DBUILD_EXAMPLES=ON \ # サンプルコードもインストールする場合 .. make -j $(nproc) make install # Meson & Ninjaを使用する場合 CXX=<G++ 8以降のG++> meson setup ./build \ -Dprefix=<toml++ライブラリのインストールディレクトリ> \ -Dbuild_examples=true # サンプルコードもインストールする場合 ninja -C ./build -j $(nproc) ninja -C ./build install
ライブラリの指定
Qtプロジェクトファイルを使用する場合
# Qtプロジェクトファイル
# pkg-configを使用してtoml++ライブラリを設定
CONFIG += link_pkgconfig
PKGCONFIG += tomlplusplus
# pkg-configを使用しない場合
LIBS += -ltomlplusplus
CMakeLists.txtファイルを使用する場合
# CMakeLists.txtファイル
# ...略
find_package(PkgConfig REQUIRED)
# toml+ライブラリの指定
pkg_check_modules(TOMLPP REQUIRED tomlplusplus)
# インクルードディレクトリの指定
target_include_directories(${PROJECT_NAME} PRIVATE
# ...略
${TOMLPP_INCLUDE_DIRS}
)
# ...略
# ライブラリのリンク
target_link_libraries(${PROJECT_NAME} PRIVATE
# ...略
${TOMLPP_LIBRARIES}
)
# コンパイルオプションの設定
target_compile_options(${PROJECT_NAME} PRIVATE
# ...略
${TOMLPP_CFLAGS_OTHER}
)
同期処理
TOMLの読み込み
以下の例では、toml++ライブラリを使用して、TOMLファイルを読み込んでいる。
toml++ライブラリを使用する場合は、プロジェクトにtoml++ライブラリをリンクする必要がある。
#include <QFile>
#include <QTextStream>
#include <stdexcept>
#include <toml++/toml.h>
class TomlHandler
{
public:
TomlHandler() = default;
// TOMLファイルを読み込む
toml::table readToml(const QString &filename)
{
try {
return toml::parse_file(filename.toStdString());
}
catch (const toml::parse_error &err) {
throw std::runtime_error(QString("TOMLファイルの解析エラー: %1").arg(err.description().c_str()).toStdString());
}
}
// TOMLデータから特定の値を取得 (テンプレート関数)
template<typename T>
T getValue(const toml::table &data, const QString &key)
{
try {
return data[key.toStdString()].value<T>();
}
catch (const toml::type_error &err) {
throw std::runtime_error(QString("値の取得エラー: %1").arg(err.what()).toStdString());
}
}
};
以下の例では、上記のクラスを使用してTOMLファイルを読み込んでいる。
#include <QDebug>
#include "TomlHandler.h"
int main()
{
TomlHandler handler;
try {
// TOMLファイルを読み込む
auto data = handler.readToml("config.toml");
// 値を取得する
QString name = handler.getValue<QString>(data, "name");
int age = handler.getValue<int>(data, "age");
qDebug() << "Name:" << name;
qDebug() << "Age:" << age;
}
catch (const std::exception &e) {
qCritical() << "エラー: " << e.what();
return -1;
}
return 0;
}
TOMLファイルの書き込み
以下の例では、toml++ライブラリを使用して、TOMLファイルを書き込んでいる。
toml++ライブラリを使用する場合は、プロジェクトにtoml++ライブラリをリンクする必要がある。
#include <QFile>
#include <QTextStream>
#include <stdexcept>
#include <toml++/toml.h>
class TomlHandler
{
public:
TomlHandler() = default;
// TOMLファイルを読み込む
toml::table readToml(const QString &filename)
{
try {
return toml::parse_file(filename.toStdString());
}
catch (const toml::parse_error &err) {
throw std::runtime_error(QString("TOMLファイルの解析エラー: %1").arg(err.description().c_str()).toStdString());
}
}
// TOMLデータをファイルに書き込む
void writeToml(const QString &filename, const toml::table &data)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
throw std::runtime_error(QString("ファイルオープンエラー: %1").arg(filename).toStdString());
}
QTextStream out(&file);
out << QString::fromStdString(toml::toml_formatter(data).format());
file.close();
}
// TOMLデータから特定の値を取得 (テンプレート関数)
template<typename T>
T getValue(const toml::table &data, const QString &key)
{
try {
return data[key.toStdString()].value<T>();
}
catch (const toml::type_error &err) {
throw std::runtime_error(QString("値の取得エラー: %1").arg(err.what()).toStdString());
}
}
// TOMLデータに値を設定する (テンプレート関数)
template<typename T>
void setValue(toml::table &data, const QString &key, const T &value)
{
data[key.toStdString()] = value;
}
};
以下の例では、上記のクラスを使用してTOMLファイルを書き込んでいる。
#include <QDebug>
#include "TomlHandler.h"
int main()
{
TomlHandler handler;
try {
// TOMLファイルを読み込む
auto data = handler.readToml("config.toml");
// 値を取得
QString name = handler.getValue<QString>(data, "name");
int age = handler.getValue<int>(data, "age");
qDebug() << "Name:" << name;
qDebug() << "Age:" << age;
// 値を設定
handler.setValue(data, "location", "Tokyo");
// 変更したデータを書き込む
handler.writeToml("config_updated.toml", data);
}
catch (const std::exception &e) {
qCritical() << "エラー: " << e.what();
return -1;
}
return 0;
}
非同期処理
TOMLの読み込み
以下の例では、toml++ライブラリを使用して、非同期でTOMLファイルを読み込んでいる。
toml++ライブラリを使用する場合は、プロジェクトにtoml++ライブラリをリンクする必要がある。
// AsyncTomlHandler.hファイル
#include <QObject>
#include <QFile>
#include <QTextStream>
#include <QBuffer>
#include <QFuture>
#include <QtConcurrent>
#include <stdexcept>
#include <toml++/toml.h>
class AsyncTomlHandler : public QObject
{
Q_OBJECT
private:
toml::table readTomlFromDevice(QIODevice* device)
{
std::unique_ptr<QIODevice> devicePtr(device); // デバイスの自動クリーンアップを保証
return readTomlStream(device);
}
public:
explicit AsyncTomlHandler(QObject *parent = nullptr) : QObject(parent) {}
// TOMLファイルを非同期で読み込む
QFuture<toml::table> readTomlAsync(const QString& filename)
{
return QtConcurrent::run([this, filename]() {
return this->readTomlFromDevice(new QFile(filename));
});
}
// TOMLデータをストリームから読み込む
toml::table readTomlStream(QIODevice* device)
{
if (!device->open(QIODevice::ReadOnly | QIODevice::Text)) {
throw std::runtime_error("デバイスを開けません");
}
QTextStream in(device);
QString content = in.readAll();
device->close();
try {
return toml::parse(content.toStdString());
}
catch (const toml::parse_error &err) {
throw std::runtime_error(QString("TOMLデータの解析エラー: %1").arg(err.description().c_str()).toStdString());
}
}
// メモリ上のTOMLデータを読み込む
toml::table readTomlFromMemory(const QByteArray& data)
{
QBuffer buffer;
buffer.setData(data);
return readTomlStream(&buffer);
}
};
以下の例では、上記のクラスを使用してTOMLファイルを非同期で読み込んでいる。
#include <QCoreApplication>
#include <QFuture>
#include <QFutureWatcher>
#include <QDebug>
#include "AsyncTomlHandler.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
AsyncTomlHandler handler;
// 非同期でTOMLファイルを読み込む
QFuture<toml::table> readFuture = handler.readTomlAsync("config.toml");
QFutureWatcher<toml::table> readWatcher;
QObject::connect(&readWatcher, &QFutureWatcher<toml::table>::finished, [&]() {
try {
toml::table data = readWatcher.result();
qDebug() << "Name:" << QString::fromStdString(data["name"].value_or(""));
qDebug() << "Age:" << data["age"].value_or(0);
}
catch (const std::exception &e) {
qCritical() << "エラー: " << e.what();
QCoreApplication::quit();
}
});
readWatcher.setFuture(readFuture);
return a.exec();
}
TOMLファイルの書き込み
以下の例では、toml++ライブラリを使用して、非同期でTOMLファイルを書き込んでいる。
toml++ライブラリを使用する場合は、プロジェクトにtoml++ライブラリをリンクする必要がある。
// AsyncTomlHandler.hファイル
#include <QObject>
#include <QFile>
#include <QTextStream>
#include <QBuffer>
#include <QFuture>
#include <QtConcurrent>
#include <stdexcept>
#include <toml++/toml.h>
class AsyncTomlHandler : public QObject
{
Q_OBJECT
private:
toml::table readTomlFromDevice(QIODevice *device)
{
std::unique_ptr<QIODevice> devicePtr(device); // デバイスの自動クリーンアップを保証
return readTomlStream(device);
}
void writeTomlToDevice(QIODevice *device, const toml::table &data)
{
std::unique_ptr<QIODevice> devicePtr(device); // デバイスの自動クリーンアップを保証
writeTomlStream(device, data);
}
public:
explicit AsyncTomlHandler(QObject *parent = nullptr) : QObject(parent) {}
// TOMLファイルを非同期で読み込む
QFuture<toml::table> readTomlAsync(const QString &filename)
{
return QtConcurrent::run([this, filename]() {
return this->readTomlFromDevice(new QFile(filename));
});
}
// TOMLデータを非同期でファイルに書き込む
QFuture<void> writeTomlAsync(const QString &filename, const toml::table &data)
{
return QtConcurrent::run([this, filename, data]() {
this->writeTomlToDevice(new QFile(filename), data);
});
}
// TOMLデータをストリームから読み込む
toml::table readTomlStream(QIODevice *device)
{
if (!device->open(QIODevice::ReadOnly | QIODevice::Text)) {
throw std::runtime_error("デバイスを開けません");
}
QTextStream in(device);
QString content = in.readAll();
device->close();
try {
return toml::parse(content.toStdString());
}
catch (const toml::parse_error &err) {
throw std::runtime_error(QString("TOMLデータの解析エラー: %1").arg(err.description().c_str()).toStdString());
}
}
// TOMLデータをストリームに書き込む
void writeTomlStream(QIODevice *device, const toml::table &data)
{
if (!device->open(QIODevice::WriteOnly | QIODevice::Text)) {
throw std::runtime_error("デバイスを開けません");
}
QTextStream out(device);
out << QString::fromStdString(toml::toml_formatter(data).format());
device->close();
}
// メモリ上のTOMLデータを読み込む
toml::table readTomlFromMemory(const QByteArray &data)
{
QBuffer buffer;
buffer.setData(data);
return readTomlStream(&buffer);
}
// メモリ上にTOMLデータを書き込む
QByteArray writeTomlToMemory(const toml::table &data)
{
QBuffer buffer;
writeTomlStream(&buffer, data);
return buffer.buffer();
}
};
以下の例では、上記のクラスを使用して、非同期でTOMLファイルを書き込んでいる。
#include <QCoreApplication>
#include <QFuture>
#include <QFutureWatcher>
#include <QDebug>
#include "AsyncTomlHandler.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
AsyncTomlHandler handler;
// 非同期でTOMLファイルを読み込む
QFuture<toml::table> readFuture = handler.readTomlAsync("config.toml");
QFutureWatcher<toml::table> readWatcher;
QObject::connect(&readWatcher, &QFutureWatcher<toml::table>::finished, [&]() {
try {
toml::table data = readWatcher.result();
qDebug() << "Name:" << QString::fromStdString(data["name"].value_or(""));
qDebug() << "Age:" << data["age"].value_or(0);
// データを変更
data["location"] = "Tokyo";
// 非同期で変更したデータを書き込む
QFuture<void> writeFuture = handler.writeTomlAsync("config_updated.toml", data);
QFutureWatcher<void> writeWatcher;
QObject::connect(&writeWatcher, &QFutureWatcher<void>::finished, []() {
qDebug() << "書き込み完了";
QCoreApplication::quit();
});
writeWatcher.setFuture(writeFuture);
}
catch (const std::exception &e) {
qCritical() << "エラー:" << e.what();
QCoreApplication::quit();
}
});
readWatcher.setFuture(readFuture);
return a.exec();
}
TOMLファイルの書き込み : メモリ操作 (ストリーミング処理)
以下の例では、toml++ライブラリを使用して、メモリ上のTOMLデータを書き込んでいる。
toml++ライブラリを使用する場合は、プロジェクトにtoml++ライブラリをリンクする必要がある。
- メモリ上のTOMLデータを読み込む。
- 読み込んだデータを表示して、一部のデータを変更する。
- 変更したデータをメモリに書き込み、結果を表示する。
#include <QCoreApplication>
#include <QDebug>
#include "AsyncTomlHandler.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
AsyncTomlHandler handler;
// メモリ上のTOMLデータを定義
QByteArray tomlData = R"(
title = "TOMLサンプル"
[owner]
name = "山田太郎"
age = 30
[database]
enabled = true
ports = [ 8000, 8001, 8002 ]
data = [ ["delta", "phi"], [3.14] ]
temp_targets = { cpu = 79.5, case = 72.0 }
)";
try {
// メモリからTOMLデータを読み込む
toml::table data = handler.readTomlFromMemory(tomlData);
// 読み込んだデータを表示
qDebug() << "メモリから読み込んだTOMLデータ:";
qDebug() << "タイトル:" << QString::fromStdString(data["title"].value_or(""));
qDebug() << "オーナー名:" << QString::fromStdString(data["owner"]["name"].value_or(""));
qDebug() << "オーナー年齢:" << data["owner"]["age"].value_or(0);
qDebug() << "データベース有効:" << data["database"]["enabled"].value_or(false);
// データを変更
data["owner"]["location"] = "東京";
data["database"]["version"] = "1.0.0";
// 変更したデータをメモリに書き込む
QByteArray updatedData = handler.writeTomlToMemory(data);
// 更新されたデータを表示
qDebug() << "更新されたTOMLデータ:";
qDebug() << updatedData;
// 更新されたデータを再度読み込んで確認
toml::table updatedTable = handler.readTomlFromMemory(updatedData);
qDebug() << "更新後のデータ確認:";
qDebug() << "オーナー所在地:" << QString::fromStdString(updatedTable["owner"]["location"].value_or(""));
qDebug() << "データベースバージョン:" << QString::fromStdString(updatedTable["database"]["version"].value_or(""));
}
catch (const std::exception &e) {
qCritical() << "エラー: " << e.what();
}
QCoreApplication::quit();
return a.exec();
}
toml11ライブラリ
toml11ライブラリとは
toml11は、C++でTOML (Tom's Obvious, Minimal Language) ファイルを解析および生成するためのヘッダオンリーのライブラリである。
このライブラリは、C++11以降の標準に準拠しており、使いやすさと効率性を重視して設計されている。
toml11ライブラリの主な特徴として、高い性能と柔軟性が挙げられる。
TOMLパーサーは再帰下降法を用いて実装されており、エラー報告機能も備えている。
これにより、TOMLファイルの解析中に問題が発生した場合、具体的なエラー情報を得ることができる。
このライブラリは、TOMLの様々なデータ型をサポートしている。
例えば、文字列、整数、浮動小数点数、真偽値、日付時刻、配列、テーブル等を扱うことができる。
また、ユーザ定義型への変換機能も提供しており、カスタムデータ構造との連携が容易である。
toml11ライブラリの使用方法は比較的簡単である。
ヘッダファイルをインクルードして、parse
メソッドを使用することにより、TOMLファイルを読み込むことができる。
解析されたデータは、C++の標準コンテナに似た方法でアクセスできる。
また、toml11ライブラリはTOMLデータの生成もサポートしており、
C++のオブジェクトからTOML形式の文字列を作成することが可能であり、これによりTOMLファイルの出力も簡単に行うことができる。
toml11ライブラリは、依存関係が無く、ヘッダファイルのみで実装されているため、プロジェクトへの導入が容易である。
このライブラリは、設定ファイルの処理やアプリケーション間でのデータ交換等、様々なシナリオで活用できる強力なツールとなっている。
toml11ライブラリのライセンス
toml11ライブラリのライセンスはMITライセンスに準拠しているため、商用プロジェクトを含む幅広い用途で自由に使用することができる。
toml++ライブラリのインストール
パッケージ管理システムからインストール
# RHEL sudo dnf install toml11-devel # SUSE -
ソースコードからインストール
toml11ライブラリのGithubにアクセスして、ソースコードをダウンロードする。
ダウンロードしたファイルを解凍する。
tar xf toml11-<バージョン>.tar.gz cd toml11-<バージョン>
toml11ライブラリをビルドおよびインストールする。
mkdir build && cd build # CMakeコマンドを使用する場合 cmake -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=<toml11ライブラリのインストールディレクトリ> \ -DTOML11_PRECOMPILE=ON \ # スタティックライブラリをインストールする場合 .. make -j $(nproc) make install
同期処理
TOMLの読み込み
TOMLファイルの書き込み
非同期処理
TOMLの読み込み
TOMLファイルの書き込み