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

Extract data from array of tables (C++) #89

Open
HollyGS opened this issue Oct 31, 2019 · 4 comments
Open

Extract data from array of tables (C++) #89

HollyGS opened this issue Oct 31, 2019 · 4 comments

Comments

@HollyGS
Copy link

HollyGS commented Oct 31, 2019

What is the best method to output the name from the first table in the array below (name = "roll")?

When using toml::find to select a robot.segment table I have encountered an error saying it is a table, and then when treating it as a table, an error that says it is an array (see screenshots).

I have also tried changing the table names to robot_segment but still no success.

[[robot]]
name = "HelloBot"
[[robot.segment]]
name = "Roll"
type = "RotZ"
translation = [1, 2, 3]
rotation = [0.1, 0.2, 0.3]
limit = [-180, 180]

[[robot.segment]]
name = "Translation"
type = "TransZ"
translation = [0, 0, 10]
rotation = [0, 0, 0]
limit = [-40, 40]

[[robot.segment]]
name = "ElbowWrist"
type = "RotX"
translation = [4, 5, 6]
rotation = [1.1, 1.2, 1.3]
limit = [-90, 90]

Screenshot from 2019-10-30 15-53-39
Screenshot from 2019-10-30 15-55-27

@ToruNiina
Copy link
Owner

Since there is no code I don't have any idea why your code does not work, but I can show you an example.

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

int main()
{
    using namespace toml::literals;

    const auto root = u8R"(
[[robot]]
name = "HelloBot"
[[robot.segment]]
name = "Roll"
type = "RotZ"
translation = [1, 2, 3]
rotation = [0.1, 0.2, 0.3]
limit = [-180, 180]

[[robot.segment]]
name = "Translation"
type = "TransZ"
translation = [0, 0, 10]
rotation = [0, 0, 0]
limit = [-40, 40]

[[robot.segment]]
name = "ElbowWrist"
type = "RotX"
translation = [4, 5, 6]
rotation = [1.1, 1.2, 1.3]
limit = [-90, 90]
    )"_toml;

    // loop over all the `[[robot]]` defined in a file
    for(const auto& robot : toml::find<toml::array>(root, "robot"))
    {
        // loop over [[robot.segment]] defined in the n-th `[[robot]]`
        for(const auto& segment : toml::find<toml::array>(robot, "segment"))
        {
            std::cout << "name = " << toml::find<std::string>(segment, "name") << std::endl;
        }
    }
    return 0;
}

The above code outputs the following.

name = Roll
name = Translation
name = ElbowWrist

You can also access to it by toml::value::at.

std::cout << root.at("robot").at(0).at("segment").at(0).at("name") << std::endl; // "Roll"
std::cout << root.at("robot").at(0).at("segment").at(1).at("name") << std::endl; // "Translation"
std::cout << root.at("robot").at(0).at("segment").at(2).at("name") << std::endl; // "ElbowWrist"

@ToruNiina
Copy link
Owner

And okay, the error message shown in your screenshot is confusing. I will check it out later.

@ToruNiina
Copy link
Owner

I guess you did something like the following in the second screenshot.

const auto root = toml::parse("example.toml");
for(const auto& segment: toml::find(root, "robot", "segment").as_array())
{
    std::cout << toml::find(segment, "name") << std::endl;
}

This will say "bad cast to table -- the actual type is array" because [[robot]] is an array (of tables). You need to cast root.at("robot") to an array and then loop over the robots.

However, I have still no idea what did you do in the first screenshot. What did you do actually?

Did you do this?

const auto name = toml::find(root.as_array().at(0), "segment", "name");

The error message will be "bad cast to array -- the actual type is table" in this case. Here root.as_array() fails because root is a table (the file itself). As described in the spec, a TOML file always has a table at the top level of a file. The returned object from toml::parse is the table at the top. [[robot]] is an array (of tables) that is defined under the root object.

The confusing part of the error message shown in this case is that the underline shows the first line of the file. It wants to show the top level of the file and it shows the first line because the top-level table is defined implicitly, without a name. But the first line defines [[robot]] that is an array of tables defined under the top-level anonymous table. This is the confusing part.

The only thing currently I can do to make it less confusing is to point the very first character of a file as the top-level table, not the whole line though I don't think this does not make it so clear..

ToruNiina added a commit that referenced this issue Nov 1, 2019
@haykh
Copy link

haykh commented Apr 8, 2024

@ToruNiina i think this case is worth documenting on the front page:

using namespace toml::literals;

const auto raw = u8R"(
[[element]]
name = "one"
[[element]]
name = "two"
[[element]]
name = "three"
)"_toml;

const auto elements = toml::find<toml::array>(raw, "element");
const auto nelem = elements.size();
for (const auto &el : elements) {
  const auto name = toml::find<std::string>(el, "name");
}

I was trying to cast it initially as toml::find<std::vector<toml::table>> instead of toml::find<toml::array>, which of course did not work with toml::find since it simply returns an std::unordered_map. Of course this is my bad, since I had to read the documentation more carefully, but I think this example might clarify the usage for the array of tables. With the former, you can still use el.at("name") but then you have to take care of the default value separately, and cannot utilize the toml::find_or, which is a lot more convenient.

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

3 participants