I'm trying to store INI file information in a struct with this code:
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <map>
#include <vector>
using Key = std::string;
using Value = std::string;
using Section = std::map<Key, Value>;
namespace qi = boost::spirit::qi;
namespace client
{
struct IniSection
{
std::string name;
Section pair;
};
struct IniFile
{
std::vector<IniSection> sections;
};
};
namespace client
{
template <typename Iterator>
struct ini_grammar : qi::grammar<Iterator, IniFile()>
{
ini_grammar() : ini_grammar::base_type(start)
{
skipper = qi::blank | '#' >> *(qi::char_ - qi::eol);
key = +qi::char_("a-zA-Z_0-9");
value = *(qi::char_ - qi::eol);
pair = key >> '=' >> value;
section = '[' >> key >> ']' >> +qi::eol >> *(pair >> +qi::eol);
file = *section;
start = qi::skip(copy(skipper))[file];
}
using Skipper = qi::rule<Iterator>;
using KVP = std::pair<Key, Value>;
Skipper skipper;
qi::rule<Iterator, IniFile()> start;
qi::rule<Iterator, IniFile(), Skipper> file;
qi::rule<Iterator, IniSection()> section;
qi::rule<Iterator, KVP()> pair;
qi::rule<Iterator> value;
qi::rule<Iterator> key;
};
}
int main()
{
std::string const ini_section =
R"([Section]
key1 = value1
key2 = value2
)";
using It = std::string::const_iterator;
client::ini_grammar<It> grammar;
client::IniFile iniFile;
It iter = ini_section.begin(), end = ini_section.end();
bool r = parse(iter, end, grammar, iniFile);
if (iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
for (const auto& section : iniFile.sections)
{
std::cout << "[" << section.name << "]\n";
for (const auto& kvp : section.pair)
{
std::cout << kvp.first << " = " << kvp.second << "\n";
}
}
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "Stopped at position: " << std::distance(ini_section.begin(), iter) << std::endl;
std::cout << "\nRemaining input unparsed:\n" << std::string(iter, end);
std::cout << "-------------------------\n";
}
}
The problem is that the code outputs a gigantic error, and I don't understand any of it. I think that most part of the error is irrelevant for solving the problem, so I'll give the parts that I think are important.
The error
boost_1_84_0/boost/spirit/home/support/container.hpp:130:12: error: no type named ‘value_type’ in ‘struct client::IniFile’
130 | struct container_value
| ^~~~~~~~~~~~~~~
boost_1_84_0/boost/spirit/home/qi/detail/pass_container.hpp:320:66: error: no type named ‘type’ in ‘struct boost::spirit::traits::container_value<client::IniFile, void>’
320 | typedef typename traits::container_value<Attr>::type value_type;
| ^~~~~~~~~~
boost_1_84_0/boost/spirit/home/qi/detail/pass_container.hpp:333:15: error: no type named ‘type’ in ‘struct boost::spirit::traits::container_value<client::IniFile, void>’
333 | > predicate;
| ^~~~~~~~~
boost_1_84_0/boost/spirit/home/support/container.hpp:130:12: error: no type named ‘value_type’ in ‘struct client::IniSection’
130 | struct container_value
| ^~~~~~~~~~~~~~~
boost/boost_1_84_0/boost/spirit/home/qi/detail/pass_container.hpp:320:66: error: no type named ‘type’ in ‘struct boost::spirit::traits::container_value<client::IniSection, void>’
320 | typedef typename traits::container_value<Attr>::type value_type;
|
boost/boost_1_84_0/boost/spirit/home/qi/detail/pass_container.hpp:333:15: error: no type named ‘type’ in ‘struct boost::spirit::traits::container_value<client::IniSection, void>’
333 | > predicate;
| ^~~~~~~~~
I'm using the code provided on the answers of this question as a reference. I tried to use BOOST_FUSION_ADAPT_STRUCT to address the issue, but I think it does not relate to the problem, it just created more errrors.
It looks like you've been shifting things around since last time. Some of the shifts have caused confusion.
E.g.:
There's a strong disconnect between the member name
pairand its typeSection.Now instead of
std::vector<IniSection>I would 100% use an associative container like I showed before:But let's for the moment assume that you really really want to retain the input order just for sections (but not for keys, apparently). The logical change would be
Introducing your own type
IniSectionandIniFilemeans you have to teach Qi how to propagate attributes to them. I can show you that, but first let's keep it simple:However, somehow your
valueandkeyrules have dropped their attributes. This quite simply means that they cannot expose their attribute at all:Therefore no automatic propagation will occur anymore, because there's nothing to propagate. By definition all your data structures are incompatible with "no attributes". Restoring those first from my previous answer:
Now you include
That also means you stopped including
Let's also restore that from my previous answer:
At this point things compile again, but it's not parsing the entire section anymore. Looking more closely, that's because you dropped the skipper from the
IniSectionandKVPrules. (WHY?)Restoring that from my previous answer, we get a full parse:
Live On Coliru
Printing
Side Notes
You no longer even checked the parse result (
r) in main. Nor do you incorporate the check foreoiinto the parser. Adding those finishing touches:Live On Coliru
Printing the expected output
SUMMARY
You are doing good toying with the code to fit your needs as well as to "make it your own", i.e. understand it fully. However, along the way you have accrued a combination of many interfering changes that together made it impossible for yourself to see what went wrong. I have a suspicion, one thing led to another¹ and here you are.
The lesson there is: by all means go and tweak the code. A programmer cannot function if they fear to touch the code. However, help yourself with two basic principles:
This implies you can never have a change that fails to compile (you just undo that change) and will immediately spot when you break something. That doesn't necessarily mean you have to undo the change. Perhaps you needed to compensate for a change somewhere else in the code. Regardless, do not change anything unrelated until all tests pass again.
This is the Way Of The Enlightened Programmer.
¹ and perhaps you started accepting some suggestions from Copilot?