Skip to content

Commit 72c8ca8

Browse files
authored
Merge pull request #10 from SSARCandy/refactor/_values_structure
Add a method to return the keys in a section
2 parents dc0d779 + 0f7f60e commit 72c8ca8

File tree

4 files changed

+114
-81
lines changed

4 files changed

+114
-81
lines changed

example.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace bc = boost::core;
1111
int main() {
1212
INIReader r{"./test/fixtures/config.ini"};
1313

14-
const auto& v1 = r.Get<std::string>("section1", "any");
14+
const auto& v1 = r.Get<std::string>("section1", "any");
1515
const auto& v2 = r.Get<int>("section1", "any");
1616
const auto& v3 = r.Get<double>("section1", "any");
1717
const auto& v4 = r.GetVector<float>("section2", "any_vec");
@@ -32,4 +32,4 @@ int main() {
3232
std::cout << "v5 = "; for (auto& v : v5) std::cout << v << " "; std::cout << ", which is type: " << bc::demangle(typeid(v5).name()) << std::endl;
3333

3434
return 0;
35-
}
35+
}

ini/INIReader.h

Lines changed: 81 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,12 @@ class INIReader
247247
int ParseError() const;
248248

249249
// Return the list of sections found in ini file
250-
const std::set<std::string>& Sections() const;
250+
const std::set<std::string> Sections() const;
251+
252+
// Return the list of keys in the given section
253+
const std::set<std::string> Keys(std::string section) const;
254+
255+
const std::unordered_map<std::string, std::string> Get(std::string section) const;
251256

252257
template<typename T = std::string>
253258
T Get(std::string section, std::string name) const;
@@ -263,13 +268,13 @@ class INIReader
263268

264269
protected:
265270
int _error;
266-
std::map<std::string, std::string> _values;
267-
std::set<std::string> _sections;
268-
static std::string MakeKey(std::string section, std::string name);
271+
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> _values;
269272
static int ValueHandler(void* user, const char* section, const char* name, const char* value);
270273

271274
template<typename T>
272275
T Converter(std::string s) const;
276+
277+
const bool BoolConverter(std::string s) const;
273278
};
274279

275280
#endif // __INIREADER_H__
@@ -310,57 +315,69 @@ inline int INIReader::ParseError() const
310315
return _error;
311316
}
312317

313-
inline const std::set<std::string>& INIReader::Sections() const
318+
inline const std::set<std::string> INIReader::Sections() const
314319
{
315-
return _sections;
320+
std::set<std::string> retval;
321+
for (auto const& element : _values) {
322+
retval.insert(element.first);
323+
}
324+
return retval;
316325
}
317326

318-
template<typename T = std::string>
327+
inline const std::set<std::string> INIReader::Keys(std::string section) const
328+
{
329+
auto const _section = Get(section);
330+
std::set<std::string> retval;
331+
for (auto const& element : _section) {
332+
retval.insert(element.first);
333+
}
334+
return retval;
335+
}
336+
337+
inline const std::unordered_map<std::string, std::string> INIReader::Get(std::string section) const {
338+
auto const _section = _values.find(section);
339+
if (_section == _values.end()) {
340+
throw std::runtime_error("section '" + section + "' not found.");
341+
}
342+
return _section->second;
343+
}
344+
345+
template<typename T>
319346
inline T INIReader::Get(std::string section, std::string name) const {
320-
std::string key = MakeKey(section, name);
321-
if (!_values.count(key)) {
322-
throw std::runtime_error("key " + key + " not found.");
347+
auto const _section = Get(section);
348+
auto const _value = _section.find(name);
349+
350+
if (_value == _section.end()) {
351+
throw std::runtime_error("key '" + name + "' not found in section '" + section + "'.");
323352
}
324-
353+
354+
std::string value = _value->second;
355+
325356
if constexpr (std::is_same<T, std::string>()) {
326-
return _values.at(key);
357+
return value;
327358
} else if constexpr (std::is_same<T, bool>()) {
328-
std::string s{_values.at(key)};
329-
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
330-
331-
const std::unordered_map<std::string, bool> s2b{
332-
{"1", true}, {"true", true}, {"yes", true}, {"on", true},
333-
{"0", false}, {"false", false}, {"no", false}, {"off", false},
334-
};
335-
return s2b.find(s)->second;
359+
return BoolConverter(value);
336360
} else {
337-
try {
338-
return Converter<T>(_values.at(key));
339-
} catch (std::exception& e) {
340-
throw std::runtime_error("cannot parse value in " + key + " to type<T>.");
341-
}
342-
}
361+
return Converter<T>(value);
362+
};
343363
}
344364

345365
template<typename T>
346366
inline T INIReader::Get(std::string section, std::string name, T&& default_v) const {
347-
std::string key = MakeKey(section, name);
348-
if (!_values.count(key)) {
367+
try {
368+
return Get<T>(section, name);
369+
} catch(std::runtime_error &e) {
349370
return default_v;
350371
}
351-
return Get<T>(section, name);
352372
}
353373

354-
template<typename T = std::string>
374+
template<typename T>
355375
inline std::vector<T> INIReader::GetVector(std::string section, std::string name) const {
356-
std::string key = MakeKey(section, name);
357-
if (!_values.count(key)) {
358-
throw std::runtime_error("key " + key + " not found.");
359-
}
376+
std::string value = Get(section, name);
360377

361-
std::istringstream out{_values.at(key)};
378+
std::istringstream out{value};
362379
const std::vector<std::string> strs{
363-
std::istream_iterator<std::string>{out},
380+
std::istream_iterator<std::string>{out},
364381
std::istream_iterator<std::string>()
365382
};
366383
try {
@@ -370,48 +387,51 @@ inline std::vector<T> INIReader::GetVector(std::string section, std::string name
370387
}
371388
return vs;
372389
} catch (std::exception& e) {
373-
throw std::runtime_error("cannot parse value in " + key + " to vector<T>.");
390+
throw std::runtime_error("cannot parse value " + value + " to vector<T>.");
374391
}
375392
}
376393

377394
template<typename T>
378395
inline std::vector<T> INIReader::GetVector(std::string section, std::string name, std::vector<T> default_v) const {
379-
std::string key = MakeKey(section, name);
380-
if (!_values.count(key)) {
396+
try {
397+
return GetVector<T>(section, name);
398+
} catch(std::runtime_error &e) {
381399
return default_v;
382-
}
383-
return GetVector<T>(section, name);
400+
};
384401
}
385402

386-
387403
template<typename T>
388404
inline T INIReader::Converter(std::string s) const {
389-
if constexpr (std::is_same<T, std::string>()) {
390-
return s;
391-
}
392-
393-
T v{};
394-
std::istringstream _{s};
395-
_.exceptions(std::ios::failbit);
396-
397-
_ >> v;
398-
return v;
405+
try {
406+
T v{};
407+
std::istringstream _{s};
408+
_.exceptions(std::ios::failbit);
409+
_ >> v;
410+
return v;
411+
} catch(std::exception& e) {
412+
throw std::runtime_error("cannot parse value '" + s + "' to type<T>.");
413+
};
399414
}
400415

401-
inline std::string INIReader::MakeKey(const std::string section, const std::string name)
402-
{
403-
std::string key = section + "=" + name;
404-
return key;
416+
inline const bool INIReader::BoolConverter(std::string s) const {
417+
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
418+
const std::unordered_map<std::string, bool> s2b{
419+
{"1", true}, {"true", true}, {"yes", true}, {"on", true},
420+
{"0", false}, {"false", false}, {"no", false}, {"off", false},
421+
};
422+
auto const value = s2b.find(s);
423+
if (value == s2b.end()) {
424+
throw std::runtime_error("'" + s + "' is not a valid boolean value.");
425+
}
426+
return value->second;
405427
}
406428

407429
inline int INIReader::ValueHandler(void* user, const char* section, const char* name, const char* value)
408430
{
409431
INIReader* reader = (INIReader*)user;
410-
std::string key = MakeKey(section, name);
411-
if (reader->_values[key].size() > 0)
412-
reader->_values[key] += "\n";
413-
reader->_values[key] += value;
414-
reader->_sections.insert(section);
432+
if (reader->_values[section][name].size() > 0)
433+
reader->_values[section][name] += "\n";
434+
reader->_values[section][name] += value;
415435
return 1;
416436
}
417437
}

test/fixtures/config.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ not_int_arr = a b c d e
88
[section2]
99

1010
any_vec = 1 2 3
11-
doubles = 1.23 4.56
11+
doubles = 1.23 4.56

test/tests.cpp

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,22 @@
66

77
using namespace inih;
88

9+
TEST(INIReader, get_sections) {
10+
INIReader r{"./fixtures/config.ini"};
11+
12+
const std::set<std::string> ans = {"section1", "section2"};
13+
14+
EXPECT_EQ(r.Sections(), ans);
15+
}
16+
17+
TEST(INIReader, get_keys) {
18+
INIReader r{"./fixtures/config.ini"};
19+
20+
const std::set<std::string> ans = {"any", "any2", "not_int", "not_int_arr"};
21+
22+
EXPECT_EQ(r.Keys("section1"), ans);
23+
}
24+
925
TEST(INIReader, get_single_value) {
1026
INIReader r{"./fixtures/config.ini"};
1127

@@ -25,13 +41,8 @@ TEST(INIReader, get_vector) {
2541
const std::vector<int> ans1{1, 2, 3};
2642
const std::vector<std::string> ans2{"1", "2", "3"};
2743

28-
const auto& vec1 = r.GetVector<int>("section2", "any_vec");
29-
const auto& vec2 = r.GetVector<>("section2", "any_vec");
30-
31-
for (int i = 0; i < ans1.size(); ++i) {
32-
EXPECT_EQ(vec1[i], ans1[i]);
33-
EXPECT_EQ(vec2[i], ans2[i]);
34-
}
44+
ASSERT_EQ(r.GetVector<int>("section2", "any_vec"), ans1);
45+
ASSERT_EQ(r.GetVector<>("section2", "any_vec"), ans2);
3546
}
3647

3748
TEST(INIReader, get_single_value_with_default) {
@@ -58,39 +69,41 @@ TEST(INIReader, get_vector_with_default) {
5869
const auto& vec1 = r.GetVector<int>("section2", "not_exist", ans1);
5970
const auto& vec2 = r.GetVector<std::string>("section2", "not_exist", std::vector<std::string>{"1", "2", "3"});
6071
const auto& vec3 = r.GetVector<double>("section2", "doubles", std::vector<double>{0});
61-
62-
for (int i = 0; i < ans1.size(); ++i) {
63-
EXPECT_EQ(vec1[i], ans1[i]);
64-
EXPECT_EQ(vec2[i], ans2[i]);
65-
EXPECT_EQ(vec3[i], ans3[i]);
66-
}
72+
73+
ASSERT_EQ(vec1, ans1);
74+
ASSERT_EQ(vec2, ans2);
75+
ASSERT_EQ(vec3, ans3);
6776
}
6877

6978

7079
TEST(INIReader, exception) {
7180

72-
81+
7382
EXPECT_THROW(INIReader{"QQ"}, std::runtime_error); // file not found
7483
EXPECT_THROW(INIReader{"./fixtures/bad_file.ini"}, std::runtime_error); // parse error
7584

7685
INIReader r{"./fixtures/config.ini"};
7786

87+
// section not found error
88+
EXPECT_THROW(r.Get("section3"), std::runtime_error);
89+
7890
// key not found error
7991
EXPECT_THROW(r.Get<int>("section1", "not_exist"), std::runtime_error);
8092
EXPECT_THROW(r.GetVector<int>("section1", "not_exist"), std::runtime_error);
8193

8294
// parse error
8395
EXPECT_THROW(r.Get<int>("section1", "not_int"), std::runtime_error);
96+
EXPECT_THROW(r.Get<bool>("section1", "not_int"), std::runtime_error);
8497
EXPECT_THROW(r.GetVector<int>("section1", "not_int_arr"), std::runtime_error);
85-
98+
8699
}
87100

88101

89102
TEST(INIReader, read_big_file) {
90103
INIReader r{"./fixtures/bigfile.ini"};
91-
104+
92105
for (int i = 1; i <= 1000; ++i) {
93106
const auto& v = r.Get<int>("section", "key" + std::to_string(i));
94107
EXPECT_EQ(v, i);
95108
}
96-
}
109+
}

0 commit comments

Comments
 (0)