FW: [filesystem] : basic_path : Feature request

These seem like reasonable requests, although I'd have to check the std::back_inserter to be 100% sure.
I have checked this using a wrapper around path as follows template<class String, class Traits> struct rob_basicpath : boost::filesystem::basic_path<String, Traits> { typedef string_type& reference; typedef const string_type& const_reference; inline void push_back(const_reference path) { *this /= path; } }; This allowed me to use the following code as expected (which strips . characters from the path) const boost::filesystem::path path("dir1/./dir2/./dir3"); rob_path rpath; std::remove_copy(path.begin(), path.end(), std::back_inserter(rpath), ".");
Note however that path has some but not all of the characteristics of a standard container. I'd want to see persuasive use cases for adding functionality - just making path more like a std container without good reason is likely to lead to interface bloat without offsetting benefits.
My reasoning for this is as follows: 1. path is wrapper around a string, which is what would be used in the absence of path object. String supports this interface and using path should not be a step backwards. 2. A variety of tasks I envisage on a path can be neatly and succinctly implemented using std algorithms. In particular, I have several uses for copy or remove_copy to create similar paths with a slight difference. The ability to use a back_inserter allow paths to be built up from other paths or any container of strings. Without this, my solution for removing '.' involved the same code but I had to insert into a vector first and later iterate through this to create the new path.
A use case would make this [pop_back()] more compelling.
My code above to remove '.' was part of a 'canonicalize' function which removed both '.' and '..' where possible. (This may be a candidate for a convenience function within the library?) My solution was as follows std::vector<std::wstring> parts; BOOST_FOREACH(std::wstring part, path) { if (L"." == part) continue; if (L".." == part && parts.size() && L".." != parts.back()) parts.pop_back(); else parts.push_back(part); } boost::filesystem::wpath simple; BOOST_FOREACH(std::wstring part, parts) simple /= part; return simple; Whilst this is IMO reasonably elegant, I still have the extra step of building my new path in a vector, then creating the path from this. The addition of pop_back() to path would allow the path to be built up without the vector. Given that path allows the path to be considered as a container of path elements, I would like to see this extended. A reverse iterator would be nice to step back from the given path up to the root. Pop_back would equate to stepping back to the parent directory and seems a useful feature. Given that there exists a function to remove the filename, this also seems to be relatively easy to implement (as an inlined function?) I have only recently discovered filesystem::path and, given my current project's requirements for directory manipulation, this is a fantastic help. Cheers Rob

On Sat, Feb 7, 2009 at 05:55, Rob <robrimmer@atrico.net> wrote:
My code above to remove '.' was part of a 'canonicalize' function which removed both '.' and '..' where possible. (This may be a candidate for a convenience function within the library?)
In fact, boost::filesystem::canonize already exists, albeit deprecated. It doesn't appear to do anything, though, which confuses me. boost::filesystem::normalize is too much, as it does the foo/.. => . reduction, which is unsafe. p: dir1/./dir2/./dir3/../dir4 p.canonize(): dir1/./dir2/./dir3/../dir4 p.normalize(): dir1/dir2/dir4 I would like to see similar things return, though. I'd advocate a clean() member function that does the foo/./bar => foo/bar reduction and ensures that all relative paths start with ./ or ../, and a non-member expand that removes all symlinks and ..s (which would require that it have a root).

On Sat, Feb 7, 2009 at 2:55 AM, Rob <robrimmer@atrico.net> wrote:
These seem like reasonable requests, although I'd have to check the std::back_inserter to be 100% sure.
I have checked this using a wrapper around path as follows
template<class String, class Traits> struct rob_basicpath : boost::filesystem::basic_path<String, Traits> { typedef string_type& reference; typedef const string_type& const_reference;
inline void push_back(const_reference path) { *this /= path; } };
This allowed me to use the following code as expected (which strips . characters from the path)
const boost::filesystem::path path("dir1/./dir2/./dir3"); rob_path rpath; std::remove_copy(path.begin(), path.end(), std::back_inserter(rpath), ".");
Arguably the problem here isn't with basic_path, but with std::back_inserter, which insists on push_back being a member function. You could write your own path_inserter or something.
Note however that path has some but not all of the characteristics of a standard container. I'd want to see persuasive use cases for adding functionality - just making path more like a std container without good reason is likely to lead to interface bloat without offsetting benefits.
I have to second this, all path member functions that take a string apply a transformation to establish the path's invariant, it wouldn't make sense to make it feel more like a container of chars.
2. A variety of tasks I envisage on a path can be neatly and succinctly implemented using std algorithms. <snipped>
The solution is to convert it to string, do what you want with it, then convert it back to a path. Alternatively, you can store paths as strings, and only convert them to filesystem::path when you need to do some kind of path operation on them. Btw, this seems very reasonable approach, given that many paths originate as strings (as user input or read from files, or passed by 3rd party code) and end up as strings (when calling fopen, etc.) Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

Emil Dotchevski wrote:
Note however that path has some but not all of the characteristics of a standard container. I'd want to see persuasive use cases for adding functionality - just making path more like a std container without good reason is likely to lead to interface bloat without offsetting benefits.
I have to second this, all path member functions that take a string apply a transformation to establish the path's invariant, it wouldn't make sense to make it feel more like a container of chars.
The idea is to make it feel like a container of strings, each string being a path element. Sebastian

Emil Dotchevski wrote:
const boost::filesystem::path path("dir1/./dir2/./dir3"); rob_path rpath; std::remove_copy(path.begin(), path.end(), std::back_inserter(rpath), ".");
Arguably the problem here isn't with basic_path, but with std::back_inserter, which insists on push_back being a member function. You could write your own path_inserter or something.
Can't we use lambda for this? path rpath; std::remove_copy(path.begin(), path.end(), var(rpath) /= _1, ".");

Andrey Semashev wrote:
Emil Dotchevski wrote:
const boost::filesystem::path path("dir1/./dir2/./dir3"); rob_path rpath; std::remove_copy(path.begin(), path.end(), std::back_inserter(rpath), ".");
Arguably the problem here isn't with basic_path, but with std::back_inserter, which insists on push_back being a member function. You could write your own path_inserter or something.
Can't we use lambda for this?
path rpath; std::remove_copy(path.begin(), path.end(), var(rpath) /= _1, ".");
Oh, I meant something like this: for_each(path.begin(), path.end(), if_(_1 != ".") [var(rpath) /= _1]);
participants (5)
-
Andrey Semashev
-
Emil Dotchevski
-
Rob
-
Scott McMurray
-
Sebastian Redl