95

TOML for Modern C++

 4 years ago
source link: https://marzer.github.io/tomlplusplus/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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 atoml::table, whereas a failed call sees atoml::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 '"sv << *err.source().path
            << "':\n"sv << err.description()
            << "\n  ("sv << err.source().begin << ")"sv
            << std::endl;
        return 1;
    }
    
    do_stuff_with_your_config(tbl);
    return 0;
}

When exceptions are disabled parsing functions return atoml::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 '"sv << *tbl.error().source().path
            << "':\n"sv << tbl.error().description()
            << "\n  ("sv << tbl.error().source().begin << ")"sv
            << std::endl;
        return 1;
    }
    
    do_stuff_with_your_config(tbl); //toml::parse_result is convertible to toml::table
    return 0;
}

Instances oftoml::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"sv << err << std::endl;
    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'stoml::source_region and description directly from the error object (as in the examples above).

Parsing TOML directly from strings and streams

Strings and std::istreams can be read directly usingtoml::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++"
        version = "0.1.0"
        authors = ["Mark Gillard <[email protected]>"]
    
        [dependencies]
        cpp = 17
    )"sv;
    
    // parse directly from a string view:
    {
        auto tbl = toml::parse(source);
        std::cout << tbl << std::endl;
    }
    
    // parse from a string stream:
    {
        std::stringstream ss{ std::string{ source } };
        auto tbl = toml::parse(ss);
        std::cout << tbl << std::endl;
    }
    
    return 0;
}
[dependencies]
cpp = 17

[library]
authors = ["Mark Gillard <[email protected]>"]
name = "toml++"
version = "0.1.0"

... exactly as above, but twice

Traversing and manipulating data

A TOML document is a tree of values, arrays and tables, represented as thetoml::value,toml::array andtoml::table, respectively. All three inherit fromtoml::node, and can be easily accessed via thetoml::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': "sv << !!numbers << std::endl;
    std::cout << "numbers is a: "sv << numbers.type() << std::endl;
    std::cout << "numbers: "sv << numbers << std::endl;

    // 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: "sv << numbers << std::endl;
    }

    // node-views can be chained to quickly query deeper
    std::cout << "cats: "sv << tbl["animals"]["cats"] << std::endl;
    std::cout << "fish[1]: "sv << tbl["animals"]["fish"][1] << std::endl;
    
    // ...even if the element doesn't exist
    std::cout << "dinosaurs: "sv << tbl["animals"]["dinosaurs"] << std::endl; //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{ "0.5.0", "and beyond" } },
        { "repo", "https://github.com/marzer/tomlplusplus/" },
        { "author", toml::table{{
                { "name", "Mark Gillard" },
                { "github", "https://github.com/marzer" },
                { "twitter", "https://twitter.com/marzer8789" }
            }}
        },
    }};

    std::cout << "###### TOML ######"sv << std::endl;
    std::cout << tbl << std::endl << std::endl;
    
    std::cout << "###### JSON ######"sv << std::endl;
    std::cout << toml::json_formatter{ tbl } << std::endl;
    return 0;
}
###### TOML ######
cpp = [17, 20, "and beyond"]
lib = "toml++"
repo = "https://github.com/marzer/tomlplusplus/"
toml = ["0.5.0", "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" : [
        "0.5.0",
        "and beyond"
    ]
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK