SimpleIni is one header, and its feature list is really about round-tripping
Cross-platform C++ library providing a simple API to read and write INI-style configuration files
At a glance
- What is it?
- SimpleIni is a header-only C++ library for reading and writing INI files, and the feature list is not really about parsing. It is about what happens when you save a file a human edited: order is preserved, comments are preserved, and whitespace is liberal on the way in. It now installs with CMake and a Makefile that treats warnings as errors.
- Who is it for?
- Adopt SimpleIni when your C++ application reads a configuration file that people also edit by hand, because preserving comments and order is the problem it solves and most parsers do not. Skip it if you need JSON, YAML or nested structures, since INI has neither.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 7 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One header, and that is the entire installation
The Installation section is four lines, and three of them are a code block.
#include "SimpleIni.h"SimpleIni is a header-only library. No building is required to use it in your project, and the README says as much: that's it, the library is ready to use.
The repository root shows a header-based layout. `SimpleIni.h` sits alongside `CMakeLists.txt`, a `SimpleIniConfig.cmake.in` for installation, a `Doxyfile` for documentation generation, a `LICENCE.txt`, a `Makefile` and a `tests/` directory.
The README is also candid about what that costs. There is no compiled library, so there is nothing to link and no binary to match your platform. But there is also no compiled library, so every translation unit that includes the header compiles it, and a large header in a large project is compile time you pay repeatedly.
The portability claim is the oldest part of the documentation and also the most specific: it is designed explicitly to be portable to any platform and has been tested on Windows, WinCE and Linux, with the feature summary widening that to Windows from 95 to 11 including CE, Linux and macOS.
WinCE is worth noticing because it remains in the documented support list alongside Windows and Linux.
The release history is steady for a project of this age: v4.25 in November 2025, v4.26 in June 2026 and v4.27 in September 2026, with the last push on 2026-10-04. The releases move through closely spaced v4.x versions, indicating ongoing maintenance rather than infrequent major-version changes.
Order and comments survive a save, which is the actual feature
Read the feature list carefully and most of it is about not destroying the file.
Three entries are about round-tripping. It saves files with sections and keys in the same order as they were loaded. It preserves comments on the file, on the section and on keys where possible. And it supports multi-line values, meaning values with embedded newline characters.
That last one interacts with the others. A configuration file with a long command line in it, or a certificate, needs a continuation style, and if the parser handles that and the writer reproduces it, the file survives.
Then four entries about being liberal on input. Configuration files can have any newline format on all platforms. Keys and values with no section are accepted. Keys with no value are accepted. And whitespace around sections, keys and values is removed.
That asymmetry is the design: accept anything a human might have written, write back a canonical form that preserves what was there.
Two optional behaviours are worth knowing about because they change how you read a file. Multiple keys with the same name can be supported, which is how the example code distinguishes a single value from a repeated one. And case-insensitive sections and keys can be enabled, for ASCII characters only.
The ASCII limitation on case folding is the kind of detail that bites. A configuration file with a key containing a non-ASCII character will not match case-insensitively, and if your application assumes it will, you have a bug that only appears on localised installations.
The rest of the list is smaller: support for both char and wchar_t programming interfaces, MBCS and UTF-8 file encodings, ICU as the conversion library on all platforms, the system locale not needing to be UTF-8 on Linux to load a UTF-8 file, support for non-ASCII characters in sections, keys, values and comments, support for non-standard character types or encodings via user-written converter classes, support for adding and modifying values programmatically, and compiling without warnings in most compilers.
That last one connects to the build, where it is enforced rather than hoped for.
The API returns a default or a pointer you must check
The example code is where the API's contract becomes clear, and it is deliberately written as tests.
The snippets are said to be included with the distribution in the automatic tests as `ts-snippets.cpp`, so every line in the README is executed. That is a small thing that means the documentation cannot drift from the code.
Loading has two forms. `LoadFile` takes a path and `LoadData` takes a string, which is what you want when the configuration arrives from an embedded resource or a network response.
Both return an `SI_Error`, and the example's pattern is to check `rc < 0` and then assert on `SI_OK`. So error reporting is by return value rather than by exception.
Reading has two shapes, and the difference matters. `GetValue(section, key)` returns a pointer, and the example asserts it is `nullptr` for a key that does not exist. `GetValue(section, key, default)` takes a default and returns it when the key is missing, so you never have to null-check.
The multi-value case has its own form:
bool hasMulti;
pv = ini.GetValue("section1", "key2", nullptr, &hasMulti);
ASSERT_EQ(hasMulti, true);So a fourth parameter reports whether the key had multiple values, and `GetAllValues` retrieves all of them. The values can then be sorted back into their original load order with `CSimpleIniA::Entry::LoadOrder()`.
That comparator is the round-tripping idea again, expressed in code: when a key appeared several times in the file, the library remembers which order it saw them in.
Enumerating is symmetrical. `GetAllSections` and `GetAllKeys` fill a `TNamesDepend` container, which is a typedef for the library's name list type.
Writing returns a different code. Calling `SetValue("section1", nullptr, nullptr)` to add a section returns `SI_INSERTED` the first time and `SI_UPDATED` when the section already existed, and the example asserts on both to show that adding an existing section is not an error.
CMake for consumers, and one conversion mode to choose
The build section has two halves: how you build the tests, and how you consume the library from CMake.
Consuming is the short version. As a subdirectory:
add_subdirectory(simpleini)
target_link_libraries(your_target PRIVATE SimpleIni::SimpleIni)Or installed system-wide with `sudo cmake --install build` and then:
find_package(SimpleIni REQUIRED)
target_link_libraries(your_target PRIVATE SimpleIni::SimpleIni)The same `SimpleIni::SimpleIni` target either way, which is the correct outcome: a downstream project should not have to know how the library was obtained.
Then there is the conversion mode, and the note is precise about it. `SI_CONVERT_GENERIC` is not the default conversion mode. When enabled, UTF-8 conversion is built into the header and no additional source files are required.
This is where the migration note connects. Earlier releases shipped `ConvertUTF.c` and `ConvertUTF.h` for `SI_CONVERT_GENERIC` and expected you to compile them into your project. UTF-8 conversion is now inline in `SimpleIni.h`, and the instruction is to remove the old files from your build and drop links to `ConvertUTF.c`, with no replacement required.
That is a breaking change for anyone who upgraded, and it is a good one: a header-only library should not require you to compile someone else's C file. But it means an existing project that has the old files listed will fail to build in a way that looks like a missing symbol rather than a documentation problem.
The alternative to the generic conversion is ICU, which the feature list says is supported on all platforms, or your own converter classes for a non-standard character type or encoding.
For building the tests, the direct CMake route is documented too: configure with `cmake -S . -B build`, build the tests optionally, or configure with `-DBUILD_TESTING=OFF` to skip them, then run `ctest --verbose` from the build directory.
Warnings are errors and the CI matrix includes arm64
The Makefile is described as a convenience wrapper, and the first comment is careful to say the build system remains CMake. It configures `build/` and invokes cmake, ctest and clang-format from the tree root.
make help # list targets
make check # format-check, build, and test
make format # apply clang-format
make format-check # verify formatting
make test # run ctest
make clean # remove build/The `help` target lists the developer commands, which is the kind of small courtesy that makes a wrapper worth having.
`check` is the interesting composition. It runs `format-check` and then `test`, and `test` depends on `build`, which depends on `configure`. So one command does formatting verification, compilation and the test suite in the right order.
Both format targets guard against a missing tool first, checking that `clang-format` is on the command line and printing an error if it is not. `format-check` uses `--dry-run --Werror`, so formatting problems fail the command rather than rewriting files. The set of files formatted is `SimpleIni.h` plus the test sources.
Parallelism comes from `nproc`, falling back to `sysctl -n hw.ncpu` and then to 4, which covers Linux, macOS and anything else unusual.
The requirement line is where the feature list's promise about warnings is enforced. It needs cmake, a C++17 compiler, and clang-format for the format targets, and test builds use `-Werror`.
`-Werror` on the tests rather than the header is a deliberate scope choice: the library promises to compile without warnings in most compilers, and the test build is where that claim is checked on every run.
The CI configuration is spelled out with equal precision. It runs `make format-check` on Linux only, and tests run on Linux for both x64 and arm64, on Windows, on macOS, and on FreeBSD at version 14.4 through a VM on ubuntu-latest, which is currently disabled.
The arm64 entry is the one to notice. The arm64 entry gives this header-only C++ library test coverage on a 64-bit ARM environment. And the FreeBSD job is marked disabled rather than deleted, which tells you the intent was there and something about the runner was not.
Editorial conclusion
Adopt SimpleIni when your C++ application reads a configuration file that people also edit by hand, because preserving comments and order is the problem it solves and most parsers do not. Skip it if you need JSON, YAML or nested structures, since INI has neither. Verify first which conversion mode you need, since `SI_CONVERT_GENERIC` is not the default and the older `ConvertUTF.c` source files no longer exist.
Frequently asked questions
How do I use SimpleIni in a C++ project?
Include the header. SimpleIni is header-only and requires no build step, so `#include "SimpleIni.h"` is the entire installation. For CMake consumers there is also `add_subdirectory` and `find_package(SimpleIni REQUIRED)`, both linking against the `SimpleIni::SimpleIni` target.
Does SimpleIni preserve comments and ordering when it saves a file?
Yes. The feature summary states it saves files with sections and keys in the same order as they were loaded, and preserves comments on the file, section and keys where possible. Values with embedded newlines are also supported, so multi-line entries survive a round trip.
What changed about SI_CONVERT_GENERIC in recent releases?
UTF-8 conversion is now inline in `SimpleIni.h` instead of being built from separate sources. Earlier releases shipped `ConvertUTF.c` and `ConvertUTF.h` that you had to compile in, so those files and their link entries should be removed from your build, with no replacement required.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/brofield-simpleini)Was this useful?