Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The toml table order is changed #133

Closed
NPYK opened this issue Sep 28, 2020 · 7 comments
Closed

The toml table order is changed #133

NPYK opened this issue Sep 28, 2020 · 7 comments

Comments

@NPYK
Copy link

NPYK commented Sep 28, 2020

Hi,
Thank you for your developing of this library.

When I use this library to save some data to a toml file, I found if the toml file have many tables like below,
the order of the table will be changed and has no rule for every saving.
Is there a way to save the data with the original order of these tables?
I use the following code to serialize the data to the file:
toml::value tomlData = toml::parse<toml::preserve_comments, std::map>(file);
std::ofstream ofs;
ofs.open(file, std::ios::out | std::ios::binary);
ofs << tomlData << std::endl;

[Title]
Title = "TOML Configration File"
[Owner]
Author = "XXX"
[Table1]
Key1 = "234"
[Table2]
Key2 = 233

@ToruNiina
Copy link
Owner

Hi,

It seems that the problem is that you explicitly specified the type of tomlData as toml::value at the first line. Since toml::value is an alias of basic_value with default parameters, it uses unordered_map internally. You need to change it to auto (or toml::basic_value<toml::preserve_comments, std::map>, but it might be too long to write) to take the result value as a customized type.

auto tomlData = toml::parse<toml::preserve_comments, std::map>(file);
// ^
std::ofstream ofs;
ofs.open(file, std::ios::out | std::ios::binary);
ofs << tomlData << std::endl;

If you want to preserve the insertion order, you can use a std::map compatible container, like https://github.com/Tessil/ordered-map, instead of std::map. (related issue: #111)

@NPYK
Copy link
Author

NPYK commented Sep 29, 2020

Thank you, but I tried both the way by declaring the "tomlData" with "auto" or "toml::basic_value<toml::preserve_comments, std::map>", the file still cannot keep the original order, it seems ordered by the ascending order of charactor. (ABCDE...)

@ToruNiina
Copy link
Owner

Sorry for the confusing wording.

The problem of using toml::value is that, by default, it uses unordered_map that randomizes the order. If we use toml::value, the toml::parse result will be converted into toml::value and the order will be randomized. So we need to use full type (toml::basic_value<...>) or auto. Here we are changing the internal container, so we need to take care about that.

The reason why it is sorted is that std::map is a kind of binary tree and internally sorts the elements. std::map does not preserve the insertion order. The reason why I used map in the last comment is that I wanted to minimize the difference from your example. To keep the original order, we need to use another map-like container that keeps the insertion order.

And there are several implementations of a map that keeps the insertion order in github, like https://github.com/Tessil/ordered-map . To keep the original order, you can use those libraries instead of std::map. (related issue: #111)

So, I mean, you can try the following.

#include "ordered_map.hpp"
#include "toml.hpp"
#include <iostream>

int main()
{
    auto tomlData = toml::parse<toml::preserve_comments, tsl::ordered_map>("sample.toml");
    std::cout << tomlData << std::endl;
}

@NPYK
Copy link
Author

NPYK commented Sep 30, 2020

Thank you very much!

The order now is saved as expected.
The solution is the same as the related issue: #111.
Only change the parse parameter still cannot save the original order, it's nessasary to change the toml::value default parameter.

@NPYK NPYK closed this as completed Sep 30, 2020
@ToruNiina
Copy link
Owner

I'm glad the problem is solved. But I'm not sure why you need to change the definition of toml::value. Could you give me some more information about

  • the version (commit id) of this library you are using
  • the TOML file you tried (if it is not a problem)
  • the cpp code you wrote (if it is not a problem)

Actually I have tried the following code without any modification.

#include "ordered-map/include/tsl/ordered_map.h"
#include "toml.hpp"
#include <iostream>

int main()
{
    auto tomlData = toml::parse<
        toml::preserve_comments, tsl::ordered_map>("issue133.toml");

    std::cout << tomlData << std::endl;
    return 0;
}
[table3]
[table3.3]
key2 = "foo"
key1 = "bar"
[table3.2]
[table3.1]

[table2]
[table2.2]
[table2.3]
[table2.1]

[table1]
[table1.3]
[table1.1]
[table1.2]

And the order seems to be preserved. I want to make it easier (and because of some curiosity), so if it is not too much trouble, I would like to see the code that does not work.

@NPYK
Copy link
Author

NPYK commented Oct 15, 2020

Sorry for late reply, as the project is too large, so I'll try to reproduce it with a new simple project and later share with you.

@NPYK
Copy link
Author

NPYK commented Oct 15, 2020

Ther verison of toml11 I used is the latest code I guess it's v3.6.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants