Boost logo

Boost :

Subject: Re: [boost] [Filesystem] v3 path separator changes
From: Alexander Lamaison (awl03_at_[hidden])
Date: 2013-03-24 14:03:06

Rob Stewart <robertstewart_at_[hidden]> writes:

> On Mar 23, 2013, at 12:13 PM, Alexander Lamaison <awl03_at_[hidden]> wrote:
>> Beman Dawes <bdawes_at_[hidden]> writes:
>>> On Sat, Mar 16, 2013 at 12:54 PM, Alexander Lamaison
>>> <awl03_at_[hidden]> wrote:
>>>> I'm manipulating Unix paths for use over SFTP, but doing so on Windows.
>>>> For instance I might want to append "c" to the Unix path "/a/b".
>>>> path p("/a/b");
>>>> p /= "c";
>>>> cout << p.string();
>>>> In version 2 this would output "/a/b/c" but now it produces "/a/b\c".
>>>> Why the breaking change?
>>> IIRC, it was partially requests from users and partially the
>>> realization that most users want platform independent syntax but
>>> platform dependent semantics.
>> In that case I'd expect it to output "\a\b\c". I can't think of a
>> reason why mixed slashes would ever be the right answer. It's the
>> worst of both worlds.
> You're expecting this line
> path p("/a/b");
> to parse your string into "a" / "b" when it merely stores the string
> as you gave it.

That's not true. It does parse the string and recognises "a" and "b" as
separate segments of the path. If it didn't, iteration would return
"a/b" followed by "c". I wrote a small program (included at the end) to
prove this. Here is the output:

Enter path:
string(): a/b
generic_string(): a/b
native(): a/b

Enter path:
string(): a\b
generic_string(): a/b
native(): a\b

Enter path:
string(): a/b\c
generic_string(): a/b/c
native(): a/b\c

> Otherwise, when you add this line
> p /= "c";
> you expect the previous separator to be used rather than the native
> separator it uses.

I expect the path to abstract over the separators used and, when asked
for the path as a string, return something consistent. It doesn't
matter if that means always using the native separator, as long as it
doesn't use both.

>> But, the biggest issue is that the change wasn't documented. The
>> docs make a big deal of the change from templated paths to a single
>> path class, but make no mention of this, more significant,
>> difference.
> I can't imagine that Beman would reject a documentation patch.

I'll fix the documentation once I understand the reasoning.

>>> If you would rather continue to use operator /= then change the output to
>>> cout << p.generic_string();
>> I've since discovered this string/generic_string/native triplet. I
>> don't think it's the right solution. string() should either return
>> the generic string or the native string. What it returns at the
>> moment is confusing and not very useful. Or is there a use-case I'm
>> not seeing.
> string() is returning the contents, as you instructed path to form
> it. Calling generic_string() means parse the contents and ensure all
> separators follow the generic syntax.

Constructing a path from "a/b" isn't an instruction to the library to
construct a path with forward slashes. It's an instruction to create a
path abstraction with two segments, the first "a" and the second "b".
Separators shouldn't even come into it until a string representation of
the path is requested, and the string conversion methods should make
explicit what separator to use: generic or native.

> To do what you want would require path to be modal.

I don't undestand what you mean here.

> Marking it, in this case, to do everything in the generic format first
> would have given the behavior you wanted. However, there is no such
> modality. / appends with the native separator. Construction from a
> string, or appending one, merely stores the supplied string. When you
> want a specific format after mixing those operations, call
> generic_string() or native_string().

Again, this isn't true. / appends a segment. Separators are irrevant
to a path abstraction. In version 2 this was the case. v3 muddles
it by giving seperators extra significance.



#include <boost/filesystem/path.hpp>

#include <iostream>
#include <string>

using boost::filesystem::path;

using std::cin;
using std::cout;
using std::wcout;
using std::endl;
using std::string;

int main()
    for (;;)
        cout << "Enter path: " << endl;

        string path_string;
        cin >> path_string;

        path p(path_string);

        cout << "string(): " << p.string() << endl;
        cout << "generic_string(): " << p.generic_string() << endl;
        cout << "native(): ";
        wcout << p.native();
        cout << endl;

        cout << "Segments: " << endl;
        path::iterator i = p.begin();
        while (i != p.end())
            cout << "\t" << *i++ << endl;
        cout << endl;

        return 0;

Boost list run by bdawes at, gregod at, cpdaniel at, john at