Image
ReleasesC++17C++20TOML
MIT LicenseCircleCIMentioned in Awesome C++

Features

  • TOML v1.0.0-rc.1, plus optional support for some unreleased TOML features
  • C++17 (plus some C++20 features where available, e.g. experimental support for char8_t strings)
  • Proper UTF-8 handling (incl. BOM)
  • Works with or without exceptions
  • Doesn't require RTTI
  • First-class support for serializing to JSON
  • Tested on Clang, GCC and MSVC (VS2019)
  • Tested on x64, x86 and ARM

Adding toml++ to your project

It's header-only library so really all you have to do is clone the repository from GitHub and set your include paths. There's some minor configuration you can do to customize some basic library functionality, but that's totally optional.

API Documentation

You're looking at it! Browse the docs using the links at the top of the page. You can search from anywhere by pressing the TAB key.

Basic examples

Parsing TOML files

toml++ works whether you have exceptions enabled or not. For the most part the usage is the same, the main difference being how parsing errors are reported to the caller. When exceptions are enabled a successful call to a parsing function simply returns a toml::table, whereas a failed call sees a toml::parse_error thrown directly from the site of the error:

#include <iostream>
#include <fstream> //required for parse_file()
#include <toml++/toml.h>
using namespace std::string_view_literals;

int main()
{
    toml::table tbl;
    try
    {
        tbl = toml::parse_file("configuration.toml");
    }
    catch (const toml::parse_error& err)
    {
        std::cerr
            << "Error parsing file '" << *err.source().path
            << "':\n" << err.description()
            << "\n  (" << err.source().begin << ")\n";
        return 1;
    }
    
    do_stuff_with_your_config(tbl);
    return 0;
}

When exceptions are disabled parsing functions return a toml::parse_result instead and it is up to the caller to check if parsing has been successful by examining the return value:

#include <iostream>
#include <fstream> //required for parse_file()
#include <toml++/toml.h>
using namespace std::string_view_literals;

int main()
{
    toml::parse_result tbl = toml::parse_file("configuration.toml");
    if (!tbl)
    {
        std::cerr
            << "Error parsing file '" << *tbl.error().source().path
            << "':\n" << tbl.error().description()
            << "\n  (" << tbl.error().source().begin << ")\n";
        return 1;
    }
    
    do_stuff_with_your_config(tbl); //toml::parse_result is convertible to toml::table
    return 0;
}

Instances of toml::parse_error can be printed directly to streams:

try
{
    auto tbl = toml::parse("enabled = trUe"sv); //fails; TOML booleans are case-sensitive
}
catch (const toml::parse_error & err)
{
    std::cerr << "Parsing failed:\n" << err << "\n";
    return 1;
}
Parsing failed:
Encountered unexpected character while parsing boolean; expected 'true', saw 'trU'
    (error occurred at line 1, column 13)

If the default error formatting is not be suitable for your use-case you can access the error's toml::source_region and description directly from the error object (as in the examples above).

Don't forget <fstream>!

Not everyone who uses the library is going to work directly from files, so not everybody is forced to pay the compilation overhead of including <fstream>. You need to explicitly include it if you're going to be calling toml::parse_file().

Parsing TOML directly from strings and streams

Strings and std::istreams can be read directly using toml::parse():

#include <iostream>
#include <sstream>
#include <toml++/toml.h>
using namespace std::string_view_literals;

int main()
{
    static constexpr auto source = R"(
        [library]
        name = "toml++"
        authors = ["Mark Gillard <mark.gillard@outlook.com.au>"]
    
        [dependencies]
        cpp = 17
    )"sv;
    
    // parse directly from a string view:
    {
        auto tbl = toml::parse(source);
        std::cout << tbl << "\n";
    }
    
    // parse from a string stream:
    {
        std::stringstream ss{ std::string{ source } };
        auto tbl = toml::parse(ss);
        std::cout << tbl << "\n";
    }
    
    return 0;
}
[dependencies]
cpp = 17

[library]
authors = ["Mark Gillard <mark@notarealwebsite.com>"]
name = "toml++"

... exactly as above, but twice

The error-handling semantics are the same as for toml::parse_file.

Traversing and manipulating data

A TOML document is a tree of values, arrays and tables, represented as the toml::value, toml::array and toml::table, respectively. All three inherit from toml::node, and can be easily accessed via the toml::node_view:

#include <iostream>
#include <toml++/toml.h>
using namespace std::string_view_literals;

int main()
{
    static constexpr auto source = R"(
        numbers = [ 1, 2, 3, "four", 5.0 ]
        vegetables = [ "tomato", "onion", "mushroom", "lettuce" ]
        minerals = [ "quartz", "iron", "copper", "diamond" ]

        [animals]
        cats = [ "tiger", "lion", "puma" ]
        birds = [ "macaw", "pigeon", "canary" ]
        fish = [ "salmon", "trout", "carp" ]

    )"sv;
    auto tbl = toml::parse(source);
    
    // get a view of the element 'numbers'
    auto numbers = tbl["numbers"];
    std::cout << "table has 'numbers': " << !!numbers << "\n";
    std::cout << "numbers is a: " << numbers.type() << "\n";
    std::cout << "numbers: " << numbers << "\n";

    // get the underlying array object to do some more advanced stuff
    if (auto arr = numbers.as_array())
    {
        for (auto& elem : *arr)
        {
            // visitation helps deal with the polymorphic nature of TOML data
            elem.visit([](auto&& el) noexcept
            {
                if constexpr (toml::is_number<decltype(el)>)
                    (*el)++;
                else if constexpr (toml::is_string<decltype(el)>)
                    el = "five"sv;
            });
        }
        
        // arrays are very similar to std::vector
        arr->push_back(7);
        arr->emplace_back<toml::array>(8, 9);
        std::cout << "numbers: " << numbers << "\n";
    }

    // node-views can be chained to quickly query deeper
    std::cout << "cats: " << tbl["animals"]["cats"] << "\n";
    std::cout << "fish[1]: " << tbl["animals"]["fish"][1] << "\n";
    
    // ...even if the element doesn't exist
    std::cout << "dinosaurs: " << tbl["animals"]["dinosaurs"] << "\n"; //no dinosaurs :(

    return 0;
}
table has 'numbers': true
numbers is an: array
numbers: [1, 2, 3, "four", 5.0]
numbers: [2, 3, 4, "five", 6.0, 7, [8, 9]]
cats: ["tiger", "lion", "puma"]
fish[1]: "trout"
dinosaurs:

Serializing as TOML and JSON

#include <iostream>
#include <toml++/toml.h>

int main()
{
    auto tbl = toml::table{{
        { "lib", "toml++" },
        { "cpp", toml::array{ 17, 20, "and beyond" } },
        { "toml", toml::array{ "1.0.0-rc.1", "and beyond" } },
        { "repo", "https://github.com/marzer/tomlplusplus/" },
        { "author", toml::table{{
                { "name", "Mark Gillard" },
                { "github", "https://github.com/marzer" },
                { "twitter", "https://twitter.com/marzer8789" }
            }}
        },
    }};

    // serializing as TOML is just writing it to a stream
    std::cout << "###### TOML ######" << "\n\n";
    std::cout << tbl << "\n\n";

    // serializing as JSON is _also_ just writing it to a stream, but via a json_formatter:
    std::cout << "###### JSON ######" << "\n\n";
    std::cout << toml::json_formatter{ tbl } << "\n\n";
    return 0;
}
###### TOML ######

cpp = [17, 20, "and beyond"]
lib = "toml++"
repo = "https://github.com/marzer/tomlplusplus/"
toml = ["1.0.0-rc.1", "and beyond"]

[author]
github = "https://github.com/marzer"
name = "Mark Gillard"
twitter = "https://twitter.com/marzer8789"

###### JSON ######

{
    "author" : {
        "github" : "https://github.com/marzer",
        "name" : "Mark Gillard",
        "twitter" : "https://twitter.com/marzer8789"
    },
    "cpp" : [
        17,
        20,
        "and beyond"
    ],
    "lib" : "toml++",
    "repo" : "https://github.com/marzer/tomlplusplus/",
    "toml" : [
        "1.0.0-rc.1",
        "and beyond"
    ]
}

Speeding up compilation

Because toml++ is a header-only library of nontrivial size you might find that compilation times noticeably increase after you add it to your project, especially if you add the library's header somewhere that's visible from a large number of translation units. You can counter this by disabling 'all inline' mode and explicitly controlling where the library's implementation is compiled.

Step 1: Set TOML_ALL_INLINE to 0 before including toml++

This must be the same everywhere, so either set it as a global #define in your build system, or do it manually before including toml++ in some global header that's used everywhere in your project:

// global_header_that_includes_toml++.h

#define TOML_ALL_INLINE 0
#include <toml.hpp>

Step 2: Define TOML_IMPLEMENTATION before including toml++ in one specific translation unit

// some_code_file.cpp

#define TOML_IMPLEMENTATION 
#include "global_header_that_includes_toml++.h"

Additionally, if all you need to do is serialize some code-generated TOML and don't actually need the parser at all, you can #define TOML_PARSER 0.

Contributing

Contributions are very welcome! Either by reporting issues or submitting pull requests. If you wish to submit a pull request, please see CONTRIBUTING for all the details you need to get going.

License

toml++ is licensed under the terms of the MIT license - see LICENSE.

If you're using the single-header version of the library you don't need to explicitly distribute the license file; it is embedded in the preamble at the top of the header.

Contacting the author

For bug reports and feature requests please consider using the GitHub issues system. For anything else though you're welcome to reach out via other means. In order of likely response speed: