TOML for Modern C++
Go to file
ToruNiina df314da751 change error message considering context
combinators are used with other parser-combinators. in such cases, empty
input means `not enough character`.
2018-12-06 17:03:57 +09:00
tests Merge branch 'result' into combinator 2018-12-06 12:57:58 +09:00
toml change error message considering context 2018-12-06 17:03:57 +09:00
.gitignore add CMakeLists 2017-04-19 22:47:40 +09:00
.travis.yml add --output-on-failure to .travis.yml 2017-12-11 12:12:22 +09:00
appveyor.yml fix file path 2017-05-17 14:10:39 +09:00
CMakeLists.txt Improve checks for C++11 compiler flags 2017-05-15 11:19:42 +02:00
LICENSE add license 2017-05-13 17:27:53 +09:00
PROPOSAL.md update readme and add proposal 2017-05-13 15:14:16 +09:00
README.md add a badge 2018-03-28 19:21:17 +09:00
toml.hpp MSVC 2015 can compile C++11 code, but does not define __cplusplus 2017-05-15 14:05:54 +02:00

toml11

Build Status Build status MIT License DOI

c++11 header-only toml parser depending only on c++11 standard library.

compatible to TOML v0.4.0.

How to use

Just include the file after adding correct include path.

#include <toml11/toml.hpp>

int main()
{
    /* do something ... */
}

decoding toml file

The only thing you have to do is passing filename to toml::parse function.

const std::string fname("sample.toml");
const auto data = toml::parse(fname);

In the case of file open error, it will throw std::runtime_error.

You can pass also stream to toml::parse function.

std::ifstream ifs("sample.toml");
assert(ifs.good());
const auto data = toml::parse(ifs);

If there are syntax error in the toml file, toml::parse will throw toml::syntax_error.

toml::get()

Then you can obtain the various value from the data using toml::get function.

answer = 42
pi = 3.14
numbers = [1,2,3]
time = 1979-05-27T07:32:00Z
[tab]
key = "value"
const auto answer    = toml::get<std::int64_t>(data.at("answer"));
const auto pi        = toml::get<double>(data.at("pi"));
const auto numbers   = toml::get<std::vector<int>>(data.at("numbers"));
const auto timepoint = toml::get<std::chrono::system_clock::time_point>(data.at("time"));
const auto tab       = toml::get<toml::Table>(data.at("tab"));
const auto key       = toml::get<std::string>(tab.at("key"));

You can set any kind of container class to obtain toml::Array except for map-like class.

const auto vc  = toml::get<std::vector<int>>(data.at("numbers"));
const auto ls  = toml::get<std::list<int>>(data.at("numbers"));
const auto dq  = toml::get<std::deque<int>>(data.at("numbers"));
const auto ar  = toml::get<std::array<int, 3>>(data.at("numbers"));
// if size of data.at("numbers") is larger than 3, it will throw toml::type_error
// because std::array is not resizable.

If the type you passed as a template parameter is incorrect, it will throw toml::type_error.

const auto wrong1 = toml::get<bool>(data.at("integer")); // exception thrown!
const auto wrong2 = toml::get<float>(data.at("integer"));        // ditto
const auto wrong3 = toml::get<toml::Datetime>(data.at("array")); // ditto

Although toml::get is convenient, it has additional copy-cost because it copies data contained in toml::value to user-specified type. Of course in some case this overhead is not ignorable.

You can get reference pointing to contained value using toml::value::cast() like this.

const auto& pi      = data.at("pi").cast<toml::value_t::Float>();
const auto& tab     = data.at("tab").cast<toml::value_t::Table>();
const auto& numbers = data.at("numbers").cast<toml::value_t::Array>();

Unfortunately, if you use toml::value::cast to get an array, you would need to cast each element in toml::Array because toml::Array is represented as an array of toml::value.

const auto& num0 = numbers.at(0).cast<toml::value_t::Integer>();
const auto& num1 = numbers.at(1).cast<toml::value_t::Integer>();
const auto& num2 = numbers.at(2).cast<toml::value_t::Integer>();

toml::get_or

You can also set default value for toml::get.

toml::Table data; // empty table!

const auto value1 = toml::get_or(data, "key1", 42); // value1 => int 42.

toml::Integer i(123);
const auto value2 = toml::get_or(data, "key1", i);  // value2 => toml::Integer 42.

toml::value_t

When you don't know the exact type of toml-value, you can get enum type from toml::value.

int i;
double d;
std::string s;
std::vector<int> a;
const auto t = data.at("something").type();
switch(t)
{
    case toml::value_t::Integer: i = toml::get<int>(data.at("something")); break;
    case toml::value_t::Float  : d = toml::get<double>(data.at("something")); break;
    case toml::value_t::String : s = toml::get<std::string>(data.at("something")); break;
    case toml::value_t::Array  : a = toml::get<std::vector<int>>(data.at("something")); break;
    default : throw std::runtime_error("unexpected type : " + toml::stringize(t));
}

Okey, but it is painful to write switch-case for many time.

toml::from_toml()

The more sophisticated way is using toml::from_toml and std::tie.

int i = 0;
double d = 0.;
std::string s;
std::vector<int> a;

toml::from_toml(std::tie(i, d, s, a), data.at("something"));

Here, only matched value will be filled. The others are left intact after calling from_toml. It should be noted that toml::from_toml returns as usual even if there are no matched type.

from_toml can be used also for single type.

int i;
toml::from_toml(i, data.at("something"));

Unlike toml::get, toml::from_toml does not require to specify the type through the template argument because the type can be deduced from argument.

toml::value

In toml, Array is capable of having Array s and each of them possibly have different types like this.

array_of_array = [[1,2,3,4,5], ["foo", "bar", "baz"]]

In this case, you can use toml::value directly.

// use toml::value in a container 
const auto a  = toml::get<std::vector<toml::value>>(data.at("array_of_array"));
// or you can use default toml::Array.
const auto a_ = toml::get<toml::Array>(data.at("array_of_array"));
// you can obtain values from toml::value in the same way as described above.
const auto ns = toml::get<std::vector<std::int64_t>>(a.at(0));
const auto ss = toml::get<std::vector<std::string>>(a.at(1));

Array of Table

Of course, you can obtain array of table in the same way.

array_of_inline_table = [{key = "value1"}, {key = "value2"}, {key = "value3"}]

[[array_of_table]]
key = "value4"
[[array_of_table]]
key = "value5"
[[array_of_table]]
key = "value6"
const auto aot1 = toml::get<std::vector<toml::Table>>(data.at("array_of_inline_table"))
const auto aot2 = toml::get<std::vector<toml::Table>>(data.at("array_of_table"))

Documentation

The toml types and corresponding enum name are listed in the table below. value_t is a scoped-enum defined in the namespace toml.

toml-type c++ type enum
Boolean bool toml::value_t::Boolean
Integer std::int64_t toml::value_t::Integer
Float double toml::value_t::Float
String std::string toml::value_t::String
Datetime toml::Datetime toml::value_t::Datetime
Array std::vector<toml::value> toml::value_t::Array
Table std::unordered_map<std::string, toml::value> toml::value_t::Table

Datetime is the struct that is defined in this library. Because std::chrono::system_clock::time_point is a time point, not capable of representing a Local Time independent from a specific day.

For user-convenience, toml::Datetime is implicitly convertible to std::chrono::system_clock::time_point. If toml::Datetime does not have any Date information, the information will be generated from std::chrono::system_clock::now() when cast is performed.

The definition of Datetime struct is below.

namespace toml
{
template<typename uintT, typename intT>
struct basic_datetime
{
    uintT  year;          // since 0.
    uintT  month;         // [1-12]
    uintT  day;           // [1-31]
    uintT  hour;          // [0-23]
    uintT  minite;        // [0-59]
    uintT  second;        // [0-59]
    uintT  millisecond    // [0-999]
    uintT  microsecond    // [0-999]
    intT   offset_hour;   // [-12 - +12]
    intT   offset_minute; // [-59 - +59]
};

typedef basic_datetime<unsigned, int> Datetime;
}

It must be noted that the range of some values in basic_datetime is different from std::tm. For example, month is in the range of [1,12] and year starts from 0 (not 1900).

Contributors

I thank the contributor for providing great feature to this repository.

  • Guillaume Fraux (@Luthaf)
    • Windows support and CI on Appvayor
    • Intel Compiler support

Licensing terms

This product is licensed under the terms of the MIT License.

  • Copyright (c) 2017 Toru Niina

All rights reserved.