comppath is a header-only C++ library for compile-time path manipulation.
All paths are processed entirely at compile time: a path is stored as a template parameter carrying a null-terminated string, and every operation is evaluated by the compiler. This means path parsing, decomposition, normalization and queries cost nothing at runtime. The resulting paths are proper compile-time constants and can be used anywhere a constant expression is required, for example as template arguments or to initialize static storage.
The library works with two path flavors, differentiated by namespace:
comppath::posix— POSIX paths, stored as UTF-8comppath::win— Windows paths, stored as UTF-16, with support for drive letters, UNC and device path root names as well as both\and/separators
Path native for the current target platform and it's operations are exported into the plain comppath namespace, so on POSIX comppath::CompPath is comppath::posix::CompPath and on Windows it is comppath::win::CompPath. The other flavor remains available under its explicit namespace, which makes it possible to reason about foreign paths (for example in cross-platform tools) on any platform.
Most operations are done via constexpr constants rather than member functions. They are named after std::filesystem::path's methods and accomplish the same goal, so the semantics should feel familiar: PARENT_PATH, FILENAME, STEM, EXTENSION, ROOT_NAME, ROOT_DIRECTORY, ROOT_PATH, RELATIVE_PATH, REMOVE_FILENAME, REPLACE_FILENAME, REPLACE_EXTENSION, LEXICALLY_NORMAL, LEXICALLY_RELATIVE and others mirror their std::filesystem::path counterparts.
using namespace comppath;
constexpr auto path = CompPath{"/usr/local/bin"};
static_assert(ROOT_PATH<path> == "/");
static_assert(PARENT_PATH<path> == "/usr/local");
static_assert(FILENAME<path> == "bin");
static_assert(LEXICALLY_NORMAL<"/usr/./local/../lib"> == "/usr/lib");Appending and concatenating paths is done with operator/ and operator+, mirroring std::filesystem::path:
constexpr auto config = CompPath{"etc"} / "app" / "app.conf"; // "etc/app/app.conf"
static_assert(REPLACE_EXTENSION<config, "cfg"> == "etc/app/app.cfg");Because constructors and operator/ have to reserve maximum possible capacity for result, some path's actual sizes may be smaller than their capacity, which can bloat binaries. In most cases inefficient storage shall not be a significant problem since operator/ only produces maximum of 1 byte of bloat if left-side path already ends with a separator (CompPath{"some/"} / "dir" ). What can produce a lot of bloat is constructing POSIX path from a UTF-32 string literal, which requires to reserve 4 times the UTF-32 codepoints amount for worst-case scenario. Other literal conversions also are also not great in that regard.
To keep your binaries efficient, apply SHRINK over constructed/appended CompPaths. Also note that all operations which are not methods of CompPath automatically apply SHRINK at the end which means that anything you get from them is stored efficiently even if an input is not optimized. Also it may be possible for some compilers to strip extra trailing zeroes from data in binary if they are not used for anything.
This project uses doxygen for documenting it's APIs. You can read docs in source code or read an index generated by xmake doxygen command. There is no remote docs index currently.
If you are an xmake user, add that to your project's xmake.lua:
add_repositories("comppath-repo [email protected]:bugsnotabunny/comppath.git v0.1.0")
add_requires("comppath v0.1.0", { external = true })More complete example may be found here
If you are using anything other then xmake, following commands will generate you code for target build system:
xmake config --toolchain=clang --mode=release --tests=n
xmake project -k (cmake|make|ninja)