relative()
operation functionSection: 6 [filesys.ts::fs.filesystem.synopsis], 15 [filesys.ts::fs.op.funcs] Status: NAD Future Submitter: GB-1 Opened: 2014-01-20 Last modified: 2016-08-10
Priority: Not Prioritized
View all other issues in [filesys.ts::fs.filesystem.synopsis].
View all issues with NAD Future status.
Discussion:
Addresses: filesys.ts
There is no relative()
operation, to complement both absolute()
and canonical()
The TS introduces relative paths.
They are defined in section 4.18 relative path [fs.def.relative-path]
A decomposition method
relative_path()
is described in section 8.4.9 path decomposition [path.decompose]
Two query methods to determine if a path
either has_relative_path()
or
is_relative()
described in 8.4.10 path query [path.query]
However there is no way to create a relative path as a path relative to another. Methods are provided to create absolute and canonical paths.
In section 15.1 Absolute [fs.op.absolute]:
path absolute(const path& p, const path& base=current_path());
and in section 15.2 Canonical [fs.op.canonical]
path canonical(const path& p, const path& base = current_path());
path canonical(const path& p, error_code& ec);
path canonical(const path& p, const path& base, error_code& ec);
By providing a operations to achieve absolute and canonical paths there is no impediment to providing a similar operation relative() that attempts to return a new path relative to some base path.
For example:
path relative(const path& p, const path& to = current_path());
path relative(const path& p, error_code& ec);
path relative(const path& p, const path& to, error_code& ec);
This would return a path, if possible, that is relative to
to
. The implementation can make use of
absolute()
and canonical()
to determine the relative path, if it exists.
The File System TS is based on the boost::filesystem library and it too suffers from this anomaly. There are open tickets for this in Boost Trac:
and it is the subject of several posts on StackOverflow for example:
http://stackoverflow.com/questions/10167382/boostfilesystem-get-relative-path
http://stackoverflow.com/questions/5772992/get-relative-path-from-two-absolute-paths
Other languages typically provide a similar function. For example python provides:
os.path.relpath(path[, start])
Return a relative filepath to path either from the current directory or from an optional start directory. This is a path computation: the filesystem is not accessed to confirm the existence or nature of path or start. start defaults to os.curdir.
[2014-02-07, Beman Dawes comments]
A relative()
function is useful and much requested.
I've seen such a function provided by users and have written it myself in app code.
It is one of those things I've been meaning to do for years, and have just never gotten around to.
That said, my mild preference is to treat this as "NAD, Future" for File System TS1, but treat it as a priority for TS2.
[ 2014-02-11 Issaquah ]
The LWG/SG-3 voted strongly in favor of adding this functionality, and doing so in this TS. That implies quite a bit of work before the next meeting to validate that the proposed interface works as desired for various platforms. There was general agreement not to hold FS STS1 if this functionality isn't ready when the rest of the TS is ready.
[2014-05-19 Beman Dawes supplied wording.]
The design benefited from discussions with Jamie Allsop, who was the source of the original NB comment. Thanks to Bjorn Reese for corrections and suggestions. Although there was also discussion and experimentation with additional relative functions that took into account symlinks and normalization, these are not proposed here since even the proponents of such functions were unsure of appropriate semantics.
[2014-06-17 Rapperswil LWG closes as NAD, Future.]
Although there is strong concensus for eventually providing both lexical and existence based flavors of relative() functionality, discussion of the many possible design choices led to the conclusion that more research and actual user experience is necessary before moving forward. Interested parties should submit papers.
Original proposed resolution:
Modify header <filesystem>
synopsis, 6 [fs.filesystem.synopsis],
by adding the operational functions after canonical
:
path relative(const path& p, const path& to = current_path()); path relative(const path& p, error_code& ec); path relative(const path& p, const path& to, error_code& ec);
Insert the section:
15.3 Relative [fs.op.relative]
path relative(const path& p, const path& to = current_path()); path relative(const path& p, error_code& ec); path relative(const path& p, const path& to, error_code& ec);Overview: Return a relative path of p to the current directory or from an optional to path.
Returns: A relative path such that
canonical(to)/relative(p,to) == canonical(p)
, otherwisepath()
. Ifcanonical(to) == canonical(p)
the pathpath(".")
is returned. For the overload without ato
argument,to
iscurrent_path()
. Signatures with argumentec
returnpath()
if an error occurs.Throws: As specified in Error reporting.
Remarks:
!exists(p) or !exists(to) or !is_directory(to)
is an error.
and bump all following sections up by 0.1. Update the contents and any cross-references accordingly.
Question: Should Returns be specified in terms of equivalence? For example:
equivalent( canonical(to)/relative(p,to), canonical(p) )
Question: Should canonical(to) == canonical(p)
return path(".")
or path()
? Why?
Question: Should to
be spelled start
?
Proposed resolution:
To 6 Header <experimental/filesystem> synopsis [fs.filesystem.synopsis], add:
path lexically_relative(const path& p, const path& base);
At the end of 8.6 path non-member functions [path.non-member], add
8.6.3
path lexically_relative(const path& p, const path& base);path
lexically_relative function [path.lexically.relative]Creates a path from the trailing elements of
p
that are lexically relative tobase
, which must be a prefix ofp
.Effects: If the number of elements in [
p.begin(), p.end()
) is less than or equal to the number of elements in [base.begin(), base.end()
), or if any element in [base.begin(), base.end()
) is not equal to the corresponding element in [p.begin(), p.end()
), throw an exception of typefilesystem_error
.Remarks: Equality or inequality are determined by
path::operator==
orpath::operator!=
respectively.Returns: An object of class
path
containing the first element ofp
that does not have a corresponding element inbase,
followed by the subsequent elements ofp
appended as if bypath::operator/=
.Throws:
filesystem_error
.[Note: Behavior is determined by the lexical value of the elements of
p
andbase
- the external file system is not accessed. The case where an element ofbase
is not equal to corresponding element ofp
is treated as an error to avoid returning an incorrect result in the event of symlinks. --end note]A possible implementation would be:
auto mm = std::mismatch( p.begin(), p.end(), base.begin(), base.end()); if (mm.first == p.end() || mm.second != base.end()) { throw filesystem_error( "p does not begin with base, so can not be made relative to base", p, base, error_code(errc::invalid_argument, generic_category())); } path tmp(*mm.first++); for (; mm.first != p.end(); ++mm.first) tmp /= *mm.first; return tmp;