Skip to content

Commit ce308be

Browse files
committed
Finish v0.1.0
This is the initial release including all necessary basic features.
2 parents 262b844 + 0579332 commit ce308be

14 files changed

Lines changed: 303 additions & 36 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
/*build*
33
/doc
44
/include/cppIni/cppini_export.h
5+
CMakeUserPresets.json

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cmake_minimum_required(VERSION 3.24)
2-
project(cppIni LANGUAGES CXX VERSION 0.0.0)
2+
project(cppIni LANGUAGES CXX VERSION 0.1.0)
33

44
set(CMAKE_CXX_STANDARD 20)
55
set(CMAKE_CXX_STANDARD_REQUIRED ON)

README.md

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
# cppIni - A C++20/23 library for reading and writing INI files
1+
# cppIni - A C++20 library for reading and writing INI files
22

3-
[![Build](https://github.com/Master92/cppIni/actions/workflows/build.yaml/badge.svg)](https://github.com/Master92/cppIni/actions/workflows/build.yaml)
3+
[![Release](https://img.shields.io/github/v/tag/Master92/cppIni?label=release)](https://github.com/Master92/cppIni/releases)
4+
[![Build](https://img.shields.io/github/actions/workflow/status/Master92/cppIni/build.yaml?logo=github)](https://github.com/Master92/cppIni/actions/workflows/build.yaml)
45
![License](https://img.shields.io/github/license/Master92/cppIni)
56
![GitHub stars](https://img.shields.io/github/stars/Master92/cppIni?label=%E2%AD%90%20Stars)
67

@@ -9,12 +10,44 @@
910
This project is a library for reading and writing INI files. It is written in C++20 and uses the STL.
1011
It is tested with GCC 13, Clang 16 and MSVC 19.36. It should work with any compiler that supports C++20.
1112

13+
### Reading and manipulating INI files
14+
15+
The library is able to read INI files and parses them automatically. It is able to read the following types:
16+
17+
- `bool`
18+
- `char`
19+
- `short`
20+
- `int`
21+
- `long`
22+
- `long long`
23+
- `unsigned char`
24+
- `unsigned short`
25+
- `unsigned int`
26+
- `unsigned long`
27+
- `unsigned long long`
28+
- `float`
29+
- `double`
30+
- `long double`
31+
- `std::string`
32+
- `std::string_view`
33+
- `const char*`
34+
35+
Accessing a value is done with the `get` template-function. It takes the section and the key as parameters and returns
36+
the value as the specified type `T`. If the value does not exist, the default value (`T()`) is returned.
37+
38+
Setting a value is done with the `set` template-function. It takes the section, the key and the value as parameters.
39+
The value is converted to a string and written to the file. If the section or the key does not exist, it is created.
40+
On every write, the file is completely rewritten.
41+
1242
## Usage
1343

1444
``` cpp
1545
#include <cppIni/cppIni.hpp>
1646

17-
Inifile ini("test.ini");
47+
File ini("test.ini");
48+
const auto intValue = init.get<int>("section", "key");
49+
const auto newValue = 42;
50+
ini.set("section", "key", newValue);
1851
```
1952
2053
## License

include/cppIni/Entry.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* cppIni - C++20/23 library for dealing with settings files
2+
* cppIni - A C++20 library for reading and writing INI files
33
* Copyright (C) 2023 Nils Hofmann <nils.friedchen@googlemail.com>
44
*
55
* This program is free software: you can redistribute it and/or modify
@@ -40,11 +40,10 @@ class CPPINI_EXPORT Entry {
4040
constexpr Entry(std::string_view key, T value, Section* parent = nullptr); ///< Constructor with key, value and parent Section pointer (default nullptr)
4141

4242
auto key() const -> std::string_view { return m_key; } ///< Key as std::string_view
43-
43+
auto fqKey() const -> std::string; ///< Fully qualified key (e.g. "Section1.Section2.Key")
4444
template<class T> auto value() const -> T; ///< Value as type T
4545
auto data() const -> std::string_view { return m_data; } ///< Value as std::string_view
4646
constexpr auto parent() const -> const Section* { return m_parent; } ///< Parent Section
47-
auto fqKey() const -> std::string; ///< Fully qualified key (e.g. "Section1.Section2.Key")
4847

4948
auto setKey(std::string_view key) -> void { m_key = key; } ///< Set the key
5049
template<class T> auto setData(T value) -> void; ///< Set the value
@@ -56,6 +55,10 @@ class CPPINI_EXPORT Entry {
5655
auto operator=(const Entry& other) -> Entry& = default; ///< Copy assignment operator
5756
auto operator=(Entry&& other) -> Entry& = default; ///< Move assignment operator
5857

58+
template<class T>
59+
requires (not std::is_same_v<T, Entry>)
60+
auto operator=(T value) -> Entry& { setData(value); return *this; } ///< Assignment operator for setting the value
61+
5962
private:
6063
std::string m_key {};
6164
std::string m_data {};
@@ -112,6 +115,7 @@ inline auto Entry::setData(std::string_view value) -> void
112115
m_data = value;
113116
}
114117

118+
template<> inline auto Entry::value<bool>() const -> bool { return std::stoi(m_data); }
115119
template<> inline auto Entry::value<char>() const -> char { return std::stoi(m_data); }
116120
template<> inline auto Entry::value<short>() const -> short { return std::stoi(m_data); }
117121
template<> inline auto Entry::value<int>() const -> int { return std::stoi(m_data); }

include/cppIni/File.h

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* cppIni - C++20/23 library for dealing with settings files
2+
* cppIni - A C++20 library for reading and writing INI files
33
* Copyright (C) 2023 Nils Hofmann <nils.friedchen@googlemail.com>
44
*
55
* This program is free software: you can redistribute it and/or modify
@@ -23,30 +23,68 @@
2323

2424
#include <filesystem>
2525
#include <vector>
26+
#include <format>
2627

2728
/// \brief Represents a file on disk.
2829
/// A file is a collection of Sections.
2930
class CPPINI_EXPORT File {
3031
public:
3132
explicit File(std::string_view filename); ///< Constructor.
32-
virtual ~File() = default; ///< Destructor.
33+
virtual ~File(); ///< Destructor.
3334

3435
static File open(std::string_view filename); ///< Open a file. Throws if the file cannot be opened.
3536
void open(); ///< Open the file. Throws if the file cannot be opened.
37+
void flush(); ///< Write the file to disk.
3638

39+
template<class T>
40+
auto set(std::string_view section, std::string_view key, T value) -> void; ///< Set a value in a section.
41+
42+
auto getSection(std::string_view fqTitle) -> Section*; ///< Get a Section by fully qualified title (e.g. "Section1.Section2")
3743
auto findSection(std::string_view title) const -> const Section*; ///< Find a Section by title.
3844
auto findEntry(std::string_view name) const -> const Entry*; ///< Find an Entry by name.
3945

46+
template<class T>
47+
auto get(std::string_view section, std::string_view name) const -> T; ///< Get an Entry by name and convert it to the specified type.
48+
4049
constexpr auto sections() const -> const auto& { return m_sections; }
4150

42-
auto operator==(const File& other) const -> bool = default; ///< Equality operator.
43-
auto operator!=(const File& other) const -> bool = default; ///< Inequality operator.
51+
auto operator==(const File& other) const -> bool; ///< Equality operator.
52+
auto operator!=(const File& other) const -> bool { return !(*this == other); }; ///< Inequality operator.
4453

4554
private:
4655
void parse(); ///< Parse the file.
4756

4857
private:
4958
std::string m_filename{};
5059

51-
std::vector<Section> m_sections{};
60+
std::vector<Section*> m_sections{};
5261
};
62+
63+
template<class T>
64+
auto File::get(std::string_view section, std::string_view name) const -> T
65+
{
66+
if (const auto entry = findEntry(std::format("{}.{}", section, name))) {
67+
return entry->value<T>();
68+
}
69+
70+
return T();
71+
}
72+
73+
/// \details The parameters are forwarded to the Section::setEntry() method. The Section is created if it does not exist.
74+
/// \arg section The title of the Section to set the value in.
75+
/// \arg key The key of the Entry to set.
76+
/// \arg value The value of the Entry to set.
77+
template<class T>
78+
auto File::set(std::string_view section, std::string_view key, T value) -> void
79+
{
80+
if (auto s = std::ranges::find_if(m_sections,
81+
[section](const auto& s) { return s->fqTitle() == section; });
82+
s != m_sections.end()) {
83+
(*s)->setEntry({key, value});
84+
} else {
85+
auto targetSection = getSection(section);
86+
targetSection->setEntry({key, value});
87+
}
88+
89+
flush();
90+
}

include/cppIni/Section.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* cppIni - C++20/23 library for dealing with settings files
2+
* cppIni - A C++20 library for reading and writing INI files
33
* Copyright (C) 2023 Nils Hofmann <nils.friedchen@googlemail.com>
44
*
55
* This program is free software: you can redistribute it and/or modify
@@ -28,7 +28,7 @@
2828
/// \note A section has a title and a list of Entry objects
2929
class CPPINI_EXPORT Section {
3030
public:
31-
explicit Section(std::string_view title, Section* parent = nullptr); ///< Constructor with title
31+
explicit Section(std::string_view title, const Section* parent = nullptr); ///< Constructor with title
3232

3333
auto title() const -> std::string_view { return m_title; } ///< Title as std::string_view
3434
auto fqTitle() const -> std::string; ///< Fully qualified title (e.g. "Section1.Section2")
@@ -37,6 +37,7 @@ class CPPINI_EXPORT Section {
3737
auto isSubsection() const -> bool { return m_parent != nullptr; } ///< Returns true if this Section is a subsection
3838

3939
auto addEntry(Entry entry) -> void; ///< Add an Entry object to the section
40+
auto setEntry(Entry entry) -> void; ///< Set an Entry object in the section and create it if it does not exist
4041

4142
template<class T>
4243
auto createEntry(std::string_view key, T value) -> void; ///< Create an Entry object in place and add it to the section
@@ -50,7 +51,7 @@ class CPPINI_EXPORT Section {
5051
private:
5152
std::string m_title;
5253
std::unordered_map<std::string, Entry> m_entries;
53-
Section *m_parent {nullptr};
54+
const Section *m_parent {nullptr};
5455
};
5556

5657
/// \details The parameters are forwarded to the Entry constructor and a pointer to this Section object is added as the parent

include/cppIni/cppIni.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* cppIni - C++20/23 library for dealing with settings files
2+
* cppIni - A C++20 library for reading and writing INI files
33
* Copyright (C) 2023 Nils Hofmann <nils.friedchen@googlemail.com>
44
*
55
* This program is free software: you can redistribute it and/or modify

src/Entry.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* cppIni - C++20/23 library for dealing with settings files
2+
* cppIni - A C++20 library for reading and writing INI files
33
* Copyright (C) 2023 Nils Hofmann <nils.friedchen@googlemail.com>
44
*
55
* This program is free software: you can redistribute it and/or modify

src/File.cpp

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* cppIni - C++20/23 library for dealing with settings files
2+
* cppIni - A C++20 library for reading and writing INI files
33
* Copyright (C) 2023 Nils Hofmann <nils.friedchen@googlemail.com>
44
*
55
* This program is free software: you can redistribute it and/or modify
@@ -18,6 +18,7 @@
1818

1919
#include <cppIni/File.h>
2020

21+
#include <algorithm>
2122
#include <fstream>
2223
#include <stdexcept>
2324

@@ -28,6 +29,15 @@ File::File(std::string_view filename)
2829
open();
2930
}
3031

32+
File::~File()
33+
{
34+
for (auto& section : m_sections) {
35+
delete section;
36+
}
37+
38+
m_sections.clear();
39+
}
40+
3141
/// \param filename The filename of the file to open.
3242
File File::open(std::string_view filename)
3343
{
@@ -44,17 +54,53 @@ void File::open()
4454
parse();
4555
}
4656

57+
void File::flush()
58+
{
59+
std::ofstream file{m_filename};
60+
61+
for (const auto& section: m_sections) {
62+
file << std::format("[{}]\n", section->fqTitle());
63+
64+
for (const auto& [_, entry]: section->entries()) {
65+
file << std::format("{}={}\n", entry.key(), entry.data());
66+
}
67+
68+
file << "\n";
69+
}
70+
}
71+
72+
/// \details Scans the vector of Sections for the parent and creates the tree including the new Section if it does not exist.
73+
/// \param fqTitle The fully qualified title of the Section to create.
74+
/// \returns A pointer to the created Section.
75+
auto File::getSection(std::string_view fqTitle) -> Section*
76+
{
77+
if (const auto section = findSection(fqTitle); section) {
78+
return const_cast<Section*>(section);
79+
}
80+
81+
if (fqTitle.find('.') == std::string_view::npos) {
82+
m_sections.emplace_back(new Section(fqTitle));
83+
return m_sections.back();
84+
} else {
85+
const auto parent = getSection(fqTitle.substr(0, fqTitle.find_last_of('.')));
86+
m_sections.emplace_back(new Section(fqTitle.substr(fqTitle.find_last_of('.') + 1), parent));
87+
return m_sections.back();
88+
}
89+
}
90+
4791
/// \param title The title of the Section to find.
4892
/// \returns A pointer to the Section if found, nullptr otherwise.
4993
auto File::findSection(std::string_view title) const -> const Section*
5094
{
51-
for (const auto& section : m_sections) {
52-
if (section.fqTitle() == title) {
53-
return &section;
54-
}
95+
const auto section = std::find_if(std::cbegin(m_sections), std::cend(m_sections), [&title](const auto& section) {
96+
return section->fqTitle() == title;
97+
});
98+
99+
if (section == std::cend(m_sections)) {
100+
return nullptr;
55101
}
56102

57-
return nullptr;
103+
return *section;
58104
}
59105

60106
/// \param name The name of the Entry to find.
@@ -75,6 +121,13 @@ auto File::findEntry(std::string_view name) const -> const Entry*
75121
return nullptr;
76122
}
77123

124+
auto File::operator==(const File& other) const -> bool
125+
{
126+
return std::equal(std::cbegin(m_sections), std::cend(m_sections), std::cbegin(other.m_sections), std::cend(other.m_sections), [](const auto& lhs, const auto& rhs) {
127+
return *lhs == *rhs;
128+
});
129+
}
130+
78131
/// \details This function is called by the constructor. It should not be called directly.
79132
/// \throws std::runtime_error if the file cannot be opened.
80133
/// \see File::open for the public function.
@@ -83,20 +136,29 @@ auto File::parse() -> void
83136
auto content = std::ifstream{m_filename};
84137

85138
for (std::string line; std::getline(content, line);) {
86-
const std::string_view lineView{line};
139+
std::string_view lineView{line};
87140

88141
if (lineView.empty()) {
89142
continue;
90143
}
91144

92-
if (line[0] == '[') {
93-
if (line.find('.') != std::string::npos) {
94-
m_sections.emplace_back(lineView.substr(lineView.find_last_of('.') + 1, lineView.find_last_of(']') - 1), &m_sections.back());
95-
} else {
96-
m_sections.emplace_back(lineView.substr(1, lineView.find_last_of(']') - 1));
145+
if (lineView[0] == '[') {
146+
lineView = lineView.substr(1);
147+
148+
Section* parent = lineView.at(0) == '.' ? m_sections.back() : nullptr;
149+
150+
if (lineView[0] == '.') {
151+
lineView = lineView.substr(1);
152+
} else if (const auto section = std::find_if(std::begin(m_sections), std::end(m_sections), [&lineView](const auto& section) {
153+
return section->fqTitle() == lineView.substr(0, lineView.find_last_of('.'));
154+
}); section != std::end(m_sections)) {
155+
parent = *section;
156+
lineView = lineView.substr(lineView.find_last_of('.') + 1);
97157
}
158+
159+
m_sections.emplace_back(new Section(lineView.substr(0, lineView.find(']')), parent));
98160
} else {
99-
m_sections.back().createEntry(lineView.substr(0, lineView.find('=')), lineView.substr(lineView.find('=') + 1));
161+
m_sections.back()->createEntry(lineView.substr(0, lineView.find('=')), lineView.substr(lineView.find('=') + 1));
100162
}
101163
}
102164
}

0 commit comments

Comments
 (0)