<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 2611: [filesys.ts] [PDTS] Lack of relative() operation function</title>
<meta property="og:title" content="Issue 2611: [filesys.ts] [PDTS] Lack of relative() operation function">
<meta property="og:description" content="C++ library issue. Status: NAD Future">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue2611.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#NAD_Future">NAD Future</a> status.</em></p>
<h3 id="2611"><a href="lwg-closed.html#2611">2611</a>. [filesys.ts] [PDTS] Lack of <code>relative()</code> operation function</h3>
<p><b>Section:</b> 6 [filesys.ts::fs.filesystem.synopsis], 15 [filesys.ts::fs.op.funcs] <b>Status:</b> <a href="lwg-active.html#NAD_Future">NAD Future</a>
 <b>Submitter:</b> GB-1 <b>Opened:</b> 2014-01-20 <b>Last modified:</b> 2016-08-10</p>
<p><b>Priority: </b>Not Prioritized
</p>
<p><b>View all other</b> <a href="lwg-index.html#filesys.ts::fs.filesystem.synopsis">issues</a> in [filesys.ts::fs.filesystem.synopsis].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#NAD Future">NAD Future</a> status.</p>
<p><b>Discussion:</b></p>
<p><b>Addresses: filesys.ts</b></p>
<p>
There is no <code>relative()</code> operation, to complement both <code>absolute()</code>
 and <code>canonical()</code>
</p>
<p>
The TS introduces relative paths. 
</p>

<ul>
  <li>
    <p>
      They are defined in section 4.18
      relative path [fs.def.relative-path]
    </p> </li>
  <li>
    <p>
      A decomposition method

      <code>relative_path()</code>
      is described in section 8.4.9 path decomposition [path.decompose]
    </p> 
  </li>
  <li>
  <p>
  
  Two query methods to determine if a path 
  either <code>has_relative_path()</code> or 
  <code>is_relative()</code> described in 8.4.10 path query [path.query] </p>
</li>
</ul>
<p>
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.
</p>
<p>

In section 15.1 Absolute [fs.op.absolute]:</p>
<p>
path absolute(const 
path&amp; 
p,
const 
path&amp; 
base=current_path());</p>
<p>

and in section 15.2 Canonical [fs.op.canonical]</p>
<p>

path canonical(const 
path&amp; 
p,
const 
path&amp; 
base = 
current_path());</p>
<p>

path canonical(const 
path&amp; 
p, 
error_code&amp; 
ec);</p>
<p>

path canonical(const 
path&amp; 
p,
const 
path&amp; 
base, 
error_code&amp; 
ec);</p>
<p>
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.</p>
<p>

For example:</p>
<p>

path relative(const 
path&amp; 
p,
const 
path&amp; 
to = 
current_path());</p>
<p>

path relative(const 
path&amp; 
p, 
error_code&amp; 
ec);</p>
<p>

path relative(const 
path&amp; 
p,
const 
path&amp; 
to, 
error_code&amp; 
ec);
</p>
<p>
This would return a path, if possible, that is relative to 
<code>to</code>. The implementation can make use of 
<code>absolute()</code> and <code>canonical()</code> 
to determine the relative path, if it exists.
</p>
<p>
The File System TS is 
based on the 
<a href="http://www.boost.org/doc/libs/1_55_0/libs/filesystem/doc/index.htm">

<u>&#8203;</u></a><a href="http://www.boost.org/doc/libs/1_55_0/libs/filesystem/doc/index.htm">boost::filesystem 
library</a> and it too suffers from this anomaly. There are open tickets for this in 
<a href="https://svn.boost.org/"><u>&#8203;</u></a><a href="https://svn.boost.org/">Boost Trac</a>:</p>
<ul>
  <li>
  <p>  
  <a href="https://svn.boost.org/trac/boost/ticket/5897">
  <u>&#8203;</u></a><a href="https://svn.boost.org/trac/boost/ticket/5897">#5897 
  Make path relative function</a>
  </p>
</li>
  <li>
  <p>  
  <a href="https://svn.boost.org/trac/boost/ticket/1976">
  <u>&#8203;</u></a><a href="https://svn.boost.org/trac/boost/ticket/1976">#1976 
  Inverse function for complete</a>
  </p>
</li>
</ul>
<p>
and it is the subject of several posts on StackOverflow for example:
</p>
<ul>
  <li>
  <p>  
  <a href="http://stackoverflow.com/questions/10167382/boostfilesystem-get-relative-path">
  <u>&#8203;</u></a><a href="http://stackoverflow.com/questions/10167382/boostfilesystem-get-relative-path">http://stackoverflow.com/questions/10167382/boostfilesystem-get-relative-path</a>
  </p>
</li>
  <li>
  <p>  
  <a href="http://stackoverflow.com/questions/5772992/get-relative-path-from-two-absolute-paths">
  <u>&#8203;</u></a><a href="http://stackoverflow.com/questions/5772992/get-relative-path-from-two-absolute-paths">http://stackoverflow.com/questions/5772992/get-relative-path-from-two-absolute-paths</a>
  </p>
</li>
</ul>
<p>
Other languages typically provide a similar function. For example python 
provides:
</p>
<p>
os.path.relpath(path[, start])
</p>
<p>
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.
</p>

<p><i>[2014-02-07, Beman Dawes comments]</i></p>


<p>A <code>relative()</code> 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.
</p>
  <p>That said, my mild preference is to treat this as "NAD, Future" for File System TS1,
  but treat it as a priority for TS2.</p>

  <p><i>[
    2014-02-11 Issaquah
  ]</i></p>

<p>
 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.
</p>

<p><i>[2014-05-19 Beman Dawes supplied wording.]</i></p>
 
<p>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.
</p>
  <p><i>[2014-06-17 Rapperswil LWG closes as NAD, Future.]</i></p>
 
<p>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.
</p>

  <p>
    <b>Original proposed resolution:</b>
  </p>
<ol>
<li>
<p>Modify header <code>&lt;filesystem&gt;</code> synopsis, 6 [fs.filesystem.synopsis],
by adding the operational functions after <code>canonical</code>:</p>
<blockquote><pre>
path relative(const path&amp; p, const path&amp; to = current_path());
path relative(const path&amp; p, error_code&amp; ec);
path relative(const path&amp; p, const path&amp; to, error_code&amp; ec);
</pre></blockquote>
</li>
<li><p>Insert the section:</p>
<blockquote><p>
15.3 Relative [fs.op.relative]
</p>
<pre>
path relative(const path&amp; p, const path&amp; to = current_path());
path relative(const path&amp; p, error_code&amp; ec);
path relative(const path&amp; p, const path&amp; to, error_code&amp; ec);</pre>
<blockquote>
<p><i>Overview</i>: Return a relative path of p to the current directory or from an optional to path.</p>
<p><i>Returns</i>: A relative path such that <code>canonical(to)/relative(p,to) == canonical(p)</code>,
otherwise <code>path()</code>. If <code>canonical(to) == canonical(p)</code> the path <code>path(".")</code> is returned. For the
overload without a <code>to</code> argument, <code>to</code> is <code>current_path()</code>. Signatures with argument <code>ec</code> return
<code>path()</code> if an error occurs.
</p>
<p>
<i>Throws</i>: As specified in Error reporting.</p>
<p>
<i>Remarks</i>: <code>!exists(p) or !exists(to) or !is_directory(to)</code> is an error.
</p>
</blockquote></blockquote>
<p>
and bump all following sections up by 0.1. Update the contents and any cross-references
accordingly.
</p>
</li>
</ol>
<p>
Question: Should Returns be specified in terms of equivalence? For example:
<code>equivalent( canonical(to)/relative(p,to), canonical(p) )</code>
</p>
<p>
Question: Should <code>canonical(to) == canonical(p)</code> return <code>path(".")</code> or <code>path()</code>? Why?
</p>
<p>

Question: Should <code>to</code> be spelled <code>start</code>?</p>

 
<p id="res-2611"><b>Proposed resolution:</b></p>

  <p>
    <i>
      To 6 Header &lt;experimental/filesystem&gt; synopsis [fs.filesystem.synopsis],
      add:
    </i>
  </p>
  <blockquote>
    <span style="background-color: #D7EEFF">path lexically_relative(const path&amp; p, const path&amp; base);</span>
  </blockquote>

  <p>
    <i>At the end of 8.6 path non-member functions [path.non-member], add </i>
  </p>
  <blockquote>

    <h4>
      8.6.3  <code>path</code>
      <span style="text-decoration: none">lexically_relative</span> function [path.lexically.relative]
    </h4>
    <span style="background-color: #D7EEFF">path lexically_relative(const path&amp; p, const path&amp; base);</span>
    <blockquote>
      <p>
        Creates a path from the trailing elements of <code>p</code> that are
        lexically relative to <code>base</code>, which must be a prefix of <code>p</code>.
      </p>
      <p>
        <i>Effects:</i> If the number of elements in [<code>
          p.begin(),
          p.end()
        </code>) is less than or equal to the number of elements in [<code>
          base.begin(),
          base.end()
        </code>), or if any element in [<code>base.begin(), base.end()</code>)
        is not equal to the corresponding element in [<code>p.begin(), p.end()</code>),
        throw an exception of type <code>filesystem_error</code>.
      </p>
      <p>
        <i>Remarks: </i>Equality or inequality are determined by <code>
          path::operator==
        </code> or <code>path::operator!=</code> respectively.
      </p>
      <p>
        <i>Returns: </i>An object of class <code>path</code> containing the first element of <code>p</code> that does not have a
        corresponding element in <code>base,</code> followed by the subsequent elements
        of <code>p</code> appended as if by <code>path::operator/=</code>.
      </p>
      <p>
        <i>Throws:</i> <code>filesystem_error</code>.
      </p>
      <p>
        [<i>Note:</i> Behavior is determined by the
        lexical value of the elements of <code>p</code> and <code>base</code> - the
        external file system is not accessed. The&nbsp;case where an element of <code>
          base
        </code>
        is not equal to corresponding element of <code>p</code> is treated as an error to avoid returning an incorrect result
        in the event of symlinks.&nbsp; <i>--end note</i>]
      </p>
    </blockquote>
    <p>
      <i>
        <span style="background-color: #CCCCCC">
          A possible implementation would
          be:
        </span>
      </i>
    </p>
    <blockquote>
      <pre>
        <span style="background-color: #CCCCCC">
auto mm = std::mismatch( p.begin(), p.end(), base.begin(), base.end());
if (mm.first == p.end() || mm.second != base.end())
{
throw filesystem_error(
&quot;p does not begin with base, so can not be made relative to base&quot;,
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;</span></pre>

    </blockquote>

  </blockquote>







</body>
</html>
