
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
That is true now.
* 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.
That is also true now, isn't it?
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'.
If I understand you correctly, you are suggesting that error checking is turned off for native paths, I would support that, but other than that I don't see how it differs. Actually, there is another case where error checking needs to be turned off: when the path is obtained from a directory_iterator, but is none the less relative (and *please* don't tell me that all such paths should be complete, that would break a lot of code; actually it would make a lot of coding idioms impossible).
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.
Not useless at all. And works now.
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).
One could add an assertion that the path is not complete, if you want that behaviour. Actually this change would break the bcp utility - there is a (slightly hairy) use for this.
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':
You should never need to set that explicitly unless you want to: each aplication inherits a default working directory anyway from the host environment. BTW the behaviour you're asking for was required by bcp - all the paths are relative to some root (the boost installation path) - that path may be relative or absolute; and whenever you need a path relative to some root, one can just use: my_root / my_relative_path so again, you can do what you want right now.
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()).
I'm sorry, but that looks way more complicated to me than the current design: if you want a path to be relative to a specific base, then use "my_base/my_path", it's easy to use, works, and it's clear what you mean as well. John.