Skip to content

Life4gal/ini

Repository files navigation

INI

Introduction

ini is a simple and easy to use ini file parser for C++.

  • Does not depend on any type and does not assume the type of data passed in by the user.

  • Supports writing the read data back to the file without losing the original comments of the file.

  • Supports writing data back to the file in the order it was read. (Newly added data is not guaranteed.)

Requirements

The front-end implementation of the parser is based on the lexy implementation.

Usage

user-defined context type

struct string_hasher
{
    using is_transparent = int;

    template<typename String>
    [[nodiscard]] constexpr auto operator()(const String& string) const noexcept -> std::size_t
    {
        if constexpr (std::is_array_v<String>)
        {
            return std::hash<std::basic_string_view<typename std::pointer_traits<std::decay_t<String>>::element_type>>{}(string);
        }
        else if constexpr (std::is_pointer_v<String>)
        {
            return std::hash<std::basic_string_view<typename std::pointer_traits<String>::element_type>>{}(string);
        }
        else if constexpr (requires { std::hash<String>{}; })
        {
            return std::hash<String>{}(string);
        }
        else
        {
            []<bool always_false = false> { static_assert(always_false, "Unsupported hash type!"); }
            ();
        }
    }
};

using group_type = std::unordered_map<std::string, std::string, string_hasher, std::equal_to<>>;
using context_type = std::unordered_map<std::string, group_type, string_hasher, std::equal_to<>>;

auto [extract_result, data] = ini::extract_from_file<context_type>("config.ini");

assert(extract_result == ini::ExtractResult::SUCCESS);
assert(not data["properties"].empty());

Extract from buffer

using group_type = std::unordered_map<std::string, std::string, string_hasher, std::equal_to<>>;
using context_type = std::unordered_map<std::string, group_type, string_hasher, std::equal_to<>>;

std::ifstream file{"config.ini", std::ios::in};
expect((file.is_open()) >> fatal);

std::string buffer{};

file.seekg(0, std::ios::end);
buffer.reserve(file.tellg());
file.seekg(0, std::ios::beg);

buffer.assign(
        std::istreambuf_iterator<char>(file),
        std::istreambuf_iterator<char>());

auto [extract_result, data] = ini::extract_from_buffer<context_type>(buffer);

assert(extract_result == ini::ExtractResult::SUCCESS);
assert(not data["properties"].empty());

Flush to user-defined type

using group_type = std::unordered_map<std::string, std::string, string_hasher, std::equal_to<>>;
using context_type = std::unordered_map<std::string, group_type, string_hasher, std::equal_to<>>;

context_type data{};

// read data...

using key_type	= context_type::key_type;
using char_type = typename ini::string_view_t<key_type>::value_type;

class MyOut final : public ini::UserOut<char_type>
{
public:
    using out_type = key_type;

private:
    out_type& out_;

public:
    explicit MyOut(out_type& out) : out_{out} {}

    /* constexpr */ auto operator<<(const char_type d) -> UserOut& override
    {
        out_.push_back(d);
        return *this;
    }

    /* constexpr */ auto operator<<(const std::basic_string_view<char_type> d) -> UserOut& override
    {
        out_.append(d);
        return *this;
    }
};

key_type buffer{};
MyOut out{buffer};

// The file path is optional, if the file exists, write `out` in the order of the content in the file, otherwise the writing order is not guaranteed.(depends on the order of `context_type)
ini::flush_to_user("config.ini", data, out);

assert(not buffer.empty());

License

See LICENSE.

TODO