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); // Fails on linux, success on windows.
fs::path p2("/usr/src/a.out, native); // Fails on windows, success 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 throw:
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 home_base(...);
// ...
fs::path runtime_rcfile(home_base);
// ...
runtime_rcfile = "myapplication/config/runtime/rc";
which is then relative to `home_base' 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