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

Table value nodes aren't set but key nodes are #237

Closed Jamesthe1 closed 1 month ago

Jamesthe1 commented 1 month ago

Environment

toml++ version and/or commit hash: 380c49f

Compiler: g++.exe (x86_64-posix-seh-rev2, Built by MinGW-W64 project) 12.2.0

C++ standard mode: 17

Target arch: x64

Relevant compilation flags: -O0 -lspdlog (spdlog if you would like to use this)

Describe the bug

Upon attempting to load a toml file, an entire table seems to not have any of its values set but the keys remain.

Steps to reproduce (or a small repro code sample)

bool try_load () {
    try {
        toml::table conf = toml::parse_file ("settings.toml");
        toml::node* graphics = conf->get ("graphics");
        if (!graphics)
            return false;

        int win_scale = graphics->as_table ()->get ("scale")->value_or (0);
        // This prints the value as expected
        spdlog::info ("Window scale: {}", win_scale);

        toml::table* binds = conf->get ("keybinds")->as_table ();
        if (!binds)
            return false;

        spdlog::info ("Binds is empty? {}", binds->empty ());
        table::iterator it = binds->begin ();
        while (it != binds->end ()) {
            spdlog::info ("{} exists: {}", it->first.data (), binds->contains (it->first));
            // Output results in every key showing "false," even with it->first.data () in contains()
            // Trying to access it->second causes a segfault
            it++;
        }
    }
    catch (exception e) {
        return false;
    }

    return true;
}

Additional information

settings.toml:

[graphics]
scale = 2

[keybinds]
game_down = 'down'
game_left = 'left'
game_right = 'right'
game_up = 'up'
pause = 'esc'

I also tried changing the value types of the keys, which also has no effect.

marzer commented 1 month ago

🤔 can you show me the output of this program when you run it locally?

marzer commented 1 month ago

When I run this code locally, (fixing the syntax errors), I see this:

Window scale: 2
Binds is empty? false
game_down exists: true
game_left exists: true
game_right exists: true
game_up exists: true
pause exists: true
marzer commented 1 month ago

Further, if I add this:

std::cout << conf["graphics"]["scale"] << "\n";
std::cout << conf["keybinds"]["game_down"] << "\n";
std::cout << conf["keybinds"]["game_left"] << "\n";
std::cout << conf["keybinds"]["game_right"] << "\n";
std::cout << conf["keybinds"]["game_up"] << "\n";
std::cout << conf["keybinds"]["pause"] << "\n";

I see this:

2
'down'
'left'
'right'
'up'
'esc'
Jamesthe1 commented 1 month ago

Strange, it seems like isolating the code into its own project fixes the issue, and spdlog has no impact on it. But there's nothing being called before loading the settings, so I'm not sure.

[2024-10-09 16:56:26.073] [info] Binds is empty? false
[2024-10-09 16:56:26.073] [info] game_down exists: false
[2024-10-09 16:56:26.073] [info] game_left exists: false
[2024-10-09 16:56:26.073] [info] game_right exists: false
[2024-10-09 16:56:26.073] [info] game_up exists: false
[2024-10-09 16:56:26.074] [info] pause exists: false

I will add I did use using directives (and tried to clean that up in the typo'd code), but changing that seems to have no effect.

Jamesthe1 commented 1 month ago

Interestingly, this issue doesn't occur if I use the [] operator like you demonstrated. By replacing the info line in the while loop with:

std::cout << conf["keybinds"][it->first.data ()].value_or (std::string ()) << "\n";

it gives me the correct data. So something about getting the node (instead of using node_views) seems very off.

marzer commented 1 month ago

So something about getting the node (instead of using node_views) seems very off.

node_view is implemented in terms of "getting the node", though. There's nothing magic going on under the hood to make these two cases behave differently - one is just a thin wrapper on top of the other.

Given that the snippet above had syntax errors (I assume because you've extracted it from a larger program and it required modification to make sense), and given it worked for you when isolated in a standalone program, are you certain there's not just a bug in your original code? Because I can't reproduce this behaviour at all. It's accounted for pretty thoroughly in the tests, and since you're building with -O0 we can likely rule out optimizer shenanigans. I'm at something of a loss 😅

One additional sanity check you could perform is debug-printing the entire table immediately after reading, e.g.:

toml::table conf = toml::parse_file("settings.toml");
std::cout << conf << '\n';

(In the original code with the issue, not the standalone one that worked.) That should hopefully cast a bit more light on whatever is going on here. You should see your original TOML echoed back at you (possibly with KVPs in a slightly different order, but otherwise the same data.

Jamesthe1 commented 1 month ago

Here's the output from the larger project that's having the issue:

[graphics]
scale = 2

[keybinds]
game_down = 'down'
game_left = 'left'
game_right = 'right'
game_up = 'up'
pause = 'esc'

Strangely, the members still don't report as existing still. I'm also at a loss.

marzer commented 1 month ago

Indeed, that is the output I'd expect, and tells me the member values are certainly there.

To reiterate my question above:

are you certain there's not just a bug in your original code?

Like, absolutely certain? Given that the snippet in the bug report is still not compilable as-is without modification, and that you fixed it when isolating it... Not wanting to say there's no toml++ bug here, necessarily, but so far it seems that's the case.

Jamesthe1 commented 1 month ago

Recently copied the original code and set it up like I did originally, even with the code that preceded it; the issue doesn't persist. It might have to do with a library doing something, but either way it seems to get into the world beyond toml++.

Thank you for the help nonetheless.

marzer commented 1 month ago

Hah, well, no worries. These things happen 😅