Boost logo

Boost :

From: Carlo Wood (carlo_at_[hidden])
Date: 2004-08-20 14:01:22


I propose the following design. The aim of boost::filesystem should be to
support the following coding idiom:

* The programmer should take care to only handle two types of paths
  in his application:
  
  1) Complete paths
  2) Relative paths

* The programmer will have to specifically tell the libary when a constructed
  path is 'native' and when not. A native path is accepted according to native
  rules and never gives any problem (exceptions) further on.
  A non-native path is checked according to the existing rules, which basically
  means that the programmer can set a default check routine that will in effect
  determine how portable the application will be.

I propose two design changes:

1) 'native' is now not only a representation, but an *internal state* of fs::path.
   (this has no effect on the representation as returned by fs::path::string()).
2) All 'complete' paths are automatically marked as 'native'.

Examples, the following code is legal:

fs::path p1("C:\\foo\\a.exe", native); // As one might do on windows.
fs::path p2("/usr/src/a.out, native); // As one might do on linux.
std::cout << p1.native_file_string(); // ok, p1 is native.

fs::path p3("foo/bar"); // Relative path, always succeeds.
std::cout << p.string(); // ok
std::cout << p.native_directory_string(); // Useless, but ok.

fs::path p4(complete(p3)); // p4 is now "native", because now it is complete.
std::cout << p4.native_directory_string(); // ok

And the following will fail (assertion?):

std::cout << p4.string() // Not allowed because p4 is native (complete).

Since there would be a default way defined of how a relative path is
completed, all operation functions will accept both, relative
and complete paths. For example:

fs::path p1("C:\\cygwin\\usr/bin/ls", native); // Legal path on Cygwin.
if (fs::exists(p1)) // Ok, access complete path.

// For clarification
fs::path p2("C:\\cygwin\\usr"); // Just an example
fs::default_working_directory(p2); // fictuous function.

fs::path p3("bin/ls"); // Portable representation (refers in fact to "C:\cygwin\usr\bin\ls.exe").
if (fs::exists(p3)) // Ok : exists() will make the path complete before testing.

And this throws:

fs::path p4("/bin/ls"); // Not allowed: this path has a root but is not marked 'native'.

Setting the default_working_directory shall allways
need to be done for each supported OS seperately.
Of course you can set it to "/" on single root machines, and
set it to "E:/" after extracting the 'E:' from the current
path at application start up, in effect simulating a 'single root':

int main()
{
  fs::default_working_directory(fs::current_path().root_path());
  // ... simulate single root machine below.

  fs::path p("bin/ls");
}

The average application will work with relative
paths, relative to some (native) base directory
and next to that have some arbitrary, complete and thus native
directories (ie, read from environment variables).

But in case more than one 'working' directory seems needed
then we can add support for that too by allowing to
construct paths with a reference to the (complete/native)
working directory. Ie,

  fs::path homedir(g_getenv("HOME"), native);
  fs::path rcdir(homedir / "edragon/rc", native);
  fs::path tmpdir(current_path().root_path() / "tmp");
  // ...

  fs::path runtime_rcfile(rcdir); // Set 'runtime_rcfile' to be relative to 'rcdir'.
  // ...

  runtime_rcfile = "config/runtimerc";

  which is then relative to `rcdir' instead of
  a single, global 'working directory' (as now returned
  by fs::current_path()).

Most of the current boost::filesystem API can be preserved with this design.

-- 
Carlo Wood <carlo_at_[hidden]>

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk