The CMake install is documented as unsupported
UTF-8 with C++ in a Portable Way
At a glance
- What is it?
- utfcpp is a header-only C++98 compatible library for handling UTF-8 encoded strings, with STL-style iterators and UTF-16 conversion. Its CMakeLists.txt exists for testing, carries an install target nobody endorses, and the author says he is considering removing it.
- Who is it for?
- utfcpp suits a C++ project that must stay compatible with an older standard or a compiler that handles Unicode badly, and that needs validation, code point counting and UTF-16 conversion over raw byte strings. It does not suit a codebase willing to standardize on one Unicode string type, and it has no answer for grapheme clusters or normalization.
- Can I use it commercially?
- Yes. BSL-1.0 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 22 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 6, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Copy a header, and ignore the build system
Installation is three steps and none of them is a build system. Download an archive into a temporary directory, unzip it, and copy the contents of the utfcpp/source directory into the place your project keeps its include files:
git clone https://github.com/nemtrif/utfcpp.gitThat is the supported way to deploy this library. The paragraph about CMake says something more interesting, because it is an author explaining accumulated drift. That file was originally made for testing purposes only, but over time contributions arrived that added an install target, and the author now writes in bold that this is not a supported way of installing the library and that he is considering removing it in a future version. The repository still carries it, along with a utf8cppConfig.cmake.in template, so the situation is a package config that exists and is explicitly not endorsed by the person who owns the project. If you are evaluating this, the install target is not a feature to rely on.
The premise is that the standard has no Unicode
The introduction states the problem the library exists to solve. C++ developers still lack an easy and portable way of handling Unicode encoded strings, because the original standard, known as C++98 or C++03, is Unicode agnostic. Later editions have made some progress, but it is still hard to work with Unicode using only standard facilities. The response is a small generic library compatible with C++98, written so that anybody used to STL algorithms and iterators finds it natural. The API shape follows from that: the introductory sample finds an invalid sequence with find_invalid over a pair of string iterators, counts code points with utf8::distance, converts to UTF-16 with utf8to16 and back with utf16to8. Note what the distance call is documented to count: Unicode code points, including the end of line and even a byte order mark if one is present, which is a detail that changes a line count in a way people are rarely told.
Two API shapes behind one macro check
The sample is written twice for every operation, once per language edition, and the conditional compilation is the interesting part. Under C++11 or later, utf8::find_invalid returns an auto and utf8to16 returns a u16string holding the whole result; before C++11 the same operations take a pair of iterators and write into a caller-supplied container through back_inserter, with the return type as a vector of unsigned short. The README explains the split as a matter of what the library exposes: if the __cplusplus macro points to C++11 or later, the API takes into account standard Unicode strings and move semantics, while an older compiler gets the same functionality in a less convenient form. The point for a user is that the same operation has two spellings and picking the wrong one is a compile error rather than a runtime surprise, so the __cplusplus check is doing real work in every translation unit that includes the header.
UTF_CPP_CPLUSPLUS overrides what the compiler claims
There is an escape hatch for the macro, and it exists for two specific reasons. Define UTF_CPP_CPLUSPLUS before including utf8.h and assign it a value for the standard you want to use; the values are the same as for __cplusplus. The first use case is not trusting __cplusplus. The second is not wanting the C++11 helper functions even on a modern compiler. The README adds a third, which is the one that matters in practice: compilers that are conservative about setting __cplusplus even when they support a recent standard edition, and Microsoft's Visual C++ is named as an example. So on some toolchains the library will quietly take the pre-C++11 path unless you say otherwise, so the result is code that still compiles and still works while taking the more verbose shape. Nothing warns you, which is why the override is documented rather than automatic.
Validating a file without reading it into memory
The second example answers a question the first one raises, which is how you check a whole file rather than a line you already have in a string. The function valid_utf8_file takes a file name, opens it, and if the stream failed to open returns false, with a comment suggesting that throwing there would be even better. It then constructs an istreambuf_iterator over the stream buffer and a matching end-of-stream sentinel, which is the standard way to iterate raw bytes without materializing a string. That matters for the case the example is written for, where content is checked without being read into memory. For a file larger than your address space, or on a platform where doubling memory for a validation pass is unacceptable, that is the difference between the library being usable and not. The trade is that the iterator pair operates on bytes rather than on characters, which is exactly why the rest of the API is iterator-based.
The intro describes the terms differently from the metadata
There is a small inconsistency in how the terms are described. The introduction says the code is freely available for any purpose and points at the LICENSE file, while the repository metadata classifies it as BSL-1.0, the Boost Software License. Those are close in spirit but not the same statement, since freely available for any purpose reads as public domain or MIT, and a reader who takes the prose rather than the file draws the wrong conclusion about what they may do with it. The author also points to a blog post, Twenty Years of my Open Source Project, for the history, and says the library has been used since the first version in 2006 in both commercial and open-source projects and proved stable and useful. That is a longevity claim worth taking seriously either way, but the terms are in the file rather than in the sentence. The published tags are 4.2.1 and 4.2.0 from August and September 2026. Full API documentation lives in API_REFERENCE.md at the repository root, and there are tests/ and bench/ directories for the two things a header-only library most needs.
Where utfcpp is the wrong choice
Four cases. The first is a codebase willing to standardize on one Unicode string type. If your project can commit to std::u16string or std::u32string as the internal representation and use the standard conversions at the boundaries, a small iterator library is a second way to do the same work. The second is JavaScript or WebAssembly interop through a toolchain that already normalizes text. UTF-16 is where this library is strongest, and a stack that has standardized on UTF-8 everywhere gains little from a conversion pass. The third is a project that needs grapheme clusters, normalization forms or full Unicode property tables. What is described here is validation, code point counting and encoding conversion, which are the byte-level operations, not the user-perceived character operations. The fourth is a build environment that insists on package managers. The supported install is a copied header, and the one build file provided is disowned by its author, so a team whose CI cannot accept a vendored single header is looking at a project that will not meet its process requirement.
Editorial conclusion
utfcpp suits a C++ project that must stay compatible with an older standard or a compiler that handles Unicode badly, and that needs validation, code point counting and UTF-16 conversion over raw byte strings. It does not suit a codebase willing to standardize on one Unicode string type, and it has no answer for grapheme clusters or normalization. Before adopting, copy the header rather than using the CMake install target the author disowns, and decide whether to define UTF_CPP_CPLUSPLUS so the C++11 overloads are compiled on every compiler including Visual C++.
Frequently asked questions
How do I install utfcpp?
Download a release archive, unzip it, and copy the contents of the utfcpp/source directory into your project's include directory. The README states this is the supported way and that the CMakeLists.txt install target is not supported.
What C++ standard does utfcpp require?
C++98. The library is written to be compatible with the original standard, and it exposes an additional API when the __cplusplus macro points to C++11 or later, using standard Unicode strings and move semantics.
What is UTF_CPP_CPLUSPLUS for?
It overrides the standard version used to compile the header. Define it before including utf8.h with the same values as __cplusplus, which is useful when you do not trust the macro, when you want to suppress the C++11 helpers, or when a compiler such as Visual C++ is conservative about setting it.
How do I check whether a file is valid UTF-8 without loading it into memory?
Open the file and iterate its stream buffer with istreambuf_iterator, as the valid_utf8_file example does, so the content is checked rather than materialized.