This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of NAD Future status.

### 2611. [filesys.ts] [PDTS] Lack of `relative()` operation function

Section: 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:

- ​#5897 Make path relative function

- ​#1976 Inverse function for complete

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:

1. 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);

2. 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)`, otherwise `path()`. If `canonical(to) == canonical(p)` the path `path(".")` is returned. For the overload without a `to` argument, `to` is `current_path()`. Signatures with argument `ec` return `path()` 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 function [path.lexically.relative]

> path lexically_relative(const path& p, const path& base);

> > Creates a path from the trailing elements of `p` that are lexically relative to `base`, which must be a prefix of `p`.

> > 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 type `filesystem_error`.

> > Remarks: Equality or inequality are determined by `path::operator==` or `path::operator!=` respectively.

> > Returns: An object of class `path` containing the first element of `p` that does not have a corresponding element in `base,` followed by the subsequent elements of `p` appended as if by `path::operator/=`.

> > Throws: `filesystem_error`.

> > [Note: Behavior is determined by the lexical value of the elements of `p` and `base` - the external file system is not accessed. The case where an element of `base` is not equal to corresponding element of `p` 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;