Let’s write our own implementation of std::filesystem. Here’s what we’re going to work from.
- C++17 draft standard - see 27.10 File systems
- File System Technical Specification - essentially what’s in the standard
- Boost Filesystem Library - the TS came from Boost, after all
- filesystem (Visual Studio 2015)
- libcstdc++ Filesystem
- Filesystem library (cppreference.com)
This shipped as
std::experimental::filesystem in Visual Studio 2015 (don’t think VS2017 promoted it to
It shipped as
std::experimental::filesystem in GCC 5.3 in libstdc++.
While it’s implemented in libc++, also as
it’s not shipping with a Clang version yet (you need to build your own Clang/libc++ to enable it).
So, let’s just write our own so we can start using it everywhere. Since we’re starting from the fact of
the C++17 standard now including it, we will just create
std::filesystem. This makes our life a little
easier at the moment, since no vendor has yet promoted it out of experimental.
This is somewhat defined for us in the standard. This goes in the namespace
wrap it like this:
inline namespace v1. See What are inline namespaces for? for an explanation about using inline namespaces to help
in API versioning.
The classes in our namespace:
There are also a host of non-member functions to implement. We’re going to group these by functionality, rather than address them all at once.
Let’s cover trivial stuff first.
The standard says that
trivial-clock is an implementation-defined type that satisfies
the C++ concept
TrivialClock (all this means is that this is a clock that would make the
chrono library happy) and is sufficient to represent the resolution and range of the file time
values offered by the filesystem. What the fuck, standard committee! Right off the bat,
we have something that could differ between filesystems and thus we can’t really pass these
values between file systems.
for something readable about
We could take the easy route. Since all the
chrono clocks meet the
we could just do this:
Let’s call that the v0 implementation.
We’re going to put a line in the sand. Our clock is 64-bit nanoseconds with a zero at 2000/01/01 00:00:00 UTC.
This has the happy property that it’s a superset of Windows
FILETIME and most Linux and Unix filesystems
that I’ve ever heard of, a superset that is losslessly convertible.
This one is easy, it’s a mandated enum class. The values aren’t mandated, but we’ll put the exceptional case as -1 and then just number others starting with 0.
Note that we end our last enum item with a comma; this is a new best practice in my opinion, because it means that adding new items can’t go awry or cause extra lines to be edited.
This one is also easy, another mandated enum class without mandated values. This is a bitmask enum, which means values will be combined together into a single field. So we just start with zero and roll up by powers of 2 to create distinct bitmaps.
Along with the enum class, we need some operators so that we can combine copy_options together; in the spec, it’s stated as a requirement that the concept BitmaskType is supported: see C++ concepts: BitmaskType.
This one is also easy, another mandated enum class but with mandated values and the operators to support the BitmaskType concept.
This one is also easy, another mandated enum class without mandated values. This is a bitmask enum, which means values will be combined together into a single field. So we just start with zero and roll up by powers of 2 to create distinct bitmaps, and add the operators as per BitmaskType.
This is defined in the standard, so hurray, but then the commentary about the
space() function itself
has a bunch of as-if POSIX, instead of just stating the obvious.
capacity is the total size of the filesystem in bytes;
free is the size of the
unallocated part of the filesystem in bytes; and
available is the number of bytes available to
a non-super user. As such,
available can be less than