OK, reboot on std::filesystem::path. There are bits of it that require C++ trickery; my first
impulse was to dive into the hard parts first, but I ran into expression SFINAE hell with
Visual Studio 2015 (which I need to support).
So instead I want to get the bulk of path done before I tackle the template functions that
have complications around them. So ignore everything said before, let’s try again.
27.10.8 Class path
value_type and preferred_separator
A path has a value_type that is platform-specific; this is meant to map directly to the
platform filesystem character set. It also has a preferred_separator that is the native
separator; this seems to have been concocted mainly for Windows support. It also has
a string_type, but this is just basic_string<value_type. So we have
This is part of what is called native format; fileystems accept and return path strings in native
format. Windows Unicode builds use UTF16LE as the filesystem character set and separate path components
with ‘\’. The actual definition of native pathname format is “The operating system dependent pathname
format accepted by the host operating system”.
27.10.8.1 Generic pathname format
Unlike the native format, generic format is specified. This is so that general-purpose code can
form and manipulate paths, presuming that there are conversion functions from native to generic,
and from generic to native.
For Windows and POSIX, this is mostly a moot point, because generic format is acceptable as a native path,
you can pass either as function arguments, and paths for files and directories share the same syntax.
This hedge exists so that other filesystems can be mapped to a common format.
On Windows, native format uses the preferred separator ‘\’, whereas generic paths prefer to join
paths with ‘/’. Obviously POSIX uses ‘/’ natively, and Windows NT filesystem functions allow ‘/’
in most cases (with the exception of so-called UNC file paths which have to start with \\\\).
27.10.8.2 path conversions
We are going to sidestep path conversions completely to begin with, since we can use either generic
paths or native paths at will in calls.
27.10.8.3 path requirements
There are a number of function templates that, in the spec, take a paramter named Source. This
is a stand-in for a family of function templates; Source is either a specialization of
basic_string or basic_string_view, or iterator_traits<decay_t<Source>>::value_type is valid
and denotes a (possibly const) encoded character type.
This applies to constructors, assignments, appends and concatenation, as these are the places
that data are passed to path.
27.10.8.4.1 path constructors
There are 5-7 different constructors, depending on how you look at it. In the following,
we are going to ignore any character conversions or normalizations that might need to be
done.
path(): construct path with empty path string
path(const path&): copy-construct from existing path
path(path&&): move-construct from existing path
The default constructor and destructor are easy, because we don’t need any state. Likewise, the
copy constructor and move constructor can rely on the copy and move constructor for our underlying
data type.
path(string_type&& source): construct from string of underlying type
Another easy constructor is the one that moves from a matching string_type.
There is a “construct from Source” template where Source can be a number of things
template<class Source> path(const Source& source): construct from Source
The Source type can be one of: basic_string, string_view, iterator of NCTCS,
or array that decays to pointer to NCTCS; NCTCS is just a fancy way of saying
“zero-terminated string”, except that string is any sequence of characters that
an iterator can step through. Since decay-to-pointer ends up with a pointer of
a certain type, we could consider this as three overloads.
We have a constructor that is passed two input iterators - this needs a type trait
in order to prevent it from matching non-iterator two-parameter constructors, just
like the single-parameter template needs a type trait to prevent it from matching
other single-parameter constructors.