marzer / tomlplusplus

Header-only TOML config file parser and serializer for C++17.
https://marzer.github.io/tomlplusplus/
MIT License
1.58k stars 152 forks source link

Explicit / Non-lexicographical sorting when serializing? #211

Closed Raildex closed 1 year ago

Raildex commented 1 year ago

Is your feature request related to a problem? Please describe. I am using TOML++ for serializing settings. The output file however has fields in alphabetical order and not in the order I add them to the toml::table initializer_list constructor.

Describe the solution you'd like Either Serializing in the order the key-value pairs arrive in the table or explicit numbering

Additional context

Table serialization (snippet)

{"graphics", toml::table{
    {"windowed",s.graphics.windowed},
    {"refresh_rate",int(s.graphics.refresh_rate)},
    {"width",int(s.graphics.resolution_width)},
    {"height",int(s.graphics.resolution_height)},
    {"animation",int(s.graphics.animation)},
    {"fog",int(s.graphics.fog)},
    {"lighting",int(s.graphics.lighting)},
    {"particle",int(s.graphics.particle)},
    {"post-processing",int(s.graphics.post_processing)},
    {"reflection",int(s.graphics.reflection)},
    {"texture",int(s.graphics.texture)},
    {"shadow",int(s.graphics.shadow)},

}}

Resulting TOML, notice how width , height, refresh_rate and windowed are scattered, despite being next to each other in the serialization:

[graphics]
animation = 2
fog = 2
height = 576
lighting = 2
particle = 2
post-processing = 2
reflection = 2
refresh_rate = 30
shadow = 2
texture = 2
width = 1024
windowed = true
marzer commented 1 year ago

This happens because TOML++ uses std::map under the hood to make use of C++17's heterogeneous lookup. In order to change this, the underlying map type would need to change, which is obviously a big ask (any drop-in replacement needs to satisfy the full interface).

Considering the TOML spec doesn't specify that key ordering needs to be maintained during serialisation round-trips, you're likely to have this (or similar) problems with different implementations too; if you want similar keys to be grouped together, could you not simply use tighter grouping in your schema? For example, put width and height as subkeys of a size key, e.g:

size.width = 1024
size.height = 768
Raildex commented 1 year ago

When I use the following:

{"graphics", toml::table{
    {"display.windowed",s.graphics.windowed},
    {"display.refresh_rate",int(s.graphics.refresh_rate)},
    {"display.resolution.width",int(s.graphics.resolution_width)},
    {"display.resolution.height",int(s.graphics.resolution_height)},
    {"quality.animation",int(s.graphics.animation)},
    {"quality.fog",int(s.graphics.fog)},
    {"quality.lighting",int(s.graphics.lighting)},
    {"quality.particle",int(s.graphics.particle)},
    {"quality.post-processing",int(s.graphics.post_processing)},
    {"quality.reflection",int(s.graphics.reflection)},
    {"quality.texture",int(s.graphics.texture)},
    {"quality.shadow",int(s.graphics.shadow)},
}},

The resulting TOML contains single-quotes around keys:

[graphics]
'display.refresh_rate' = 30
'display.resolution.height' = 450
'display.resolution.width' = 800
'display.windowed' = true
'quality.animation' = 2
'quality.fog' = 2
'quality.lighting' = 2
'quality.particle' = 2
'quality.post-processing' = 2
'quality.reflection' = 2
'quality.shadow' = 2
'quality.texture' = 2
marzer commented 1 year ago

Which is correct; by putting the dots in the keys, you're telling TOML++ you literally want dots in the keys, not that you want dotted keys (i.e. no parsing and key subdivision takes place here). To achieve what you want, you need to nest subtables inside the C++ initializer list. From the 'Serializing' example on the toml++ homepage:

    auto tbl = toml::table{
        { "lib", "toml++" },
        { "cpp", toml::array{ 17, 20, "and beyond" } },
        { "toml", toml::array{ "1.0.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" }
            }
        },
    };

Note the author key nests an additional toml::table inside of it. The above example would serialize as a nested table:

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

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

There's no way to say "emit the author subkeys as dotted keys please", though I wouldn't be averse to adding something like that.

marzer commented 1 year ago

@Raildex given that you've restructured your data to avoid this issue (per #212), do you still need this feature? Otherwise I'll close it out.

Raildex commented 1 year ago

I'll close it :)