2017-08-13 20 views
4

メンバー関数string()を使用してboost::filesystem::pathのベクトルをstd::stringに変換しようとしています。私はこれを書いて、それは、Windows(MSVC 14、2015)に罰金働いていた:これはboost :: filesystemのバグですか? boost :: filesystem :: path :: string()がWindowsとLinuxで同じシグネチャを持っていないのはなぜですか?

std::transform(
    users.begin(), users.end(), std::back_inserter(usersStrs), 
    std::mem_fn(static_cast<const std::string (PathType::*)() const>(
     &PathType::string))); 

は、今私は、GCC(6.3、Debianのストレッチ)に移動し、そして私のコードは、上記の署名が存在しないというリンクエラーが発生しました。

std::transform(
    users.begin(), users.end(), std::back_inserter(usersStrs), 
    std::mem_fn(static_cast<const std::string& (PathType::*)() const>(
     &PathType::string))) 

PS:それを修正するために、私は、コードを変更しなければならなかった私は、私が今必要性から、に切り替えており、ラムダソリューションは簡単です知っています。

最初は、MSVCの方が耐性があると思っていましたが、Windowsに切り替えて、最初の署名が正しいという反対のリンクエラーが発生しました。私は、ソースコード(1.64、path.hpp)に行って、これは私が見つけたものです:それは、デフォルトでUTF-8を使用していないので、

# ifdef BOOST_WINDOWS_API 
    const std::string string() const 
    { 
     std::string tmp; 
     if (!m_pathname.empty()) 
     path_traits::convert(&*m_pathname.begin(), &*m_pathname.begin()+m_pathname.size(), 
     tmp); 
     return tmp; 
    } 
//... 
# else // BOOST_POSIX_API 
    // string_type is std::string, so there is no conversion 
    const std::string& string() const { return m_pathname; } 
//... 
# endif 

は、だから私が見る理由は、Windows上でそれで、あります一時的な変換。しかし、なぜWindowsとLinuxの両方で同じAPIを使用しないでください。最悪の場合、文字列のコピーが必要になります。右?

path::string()の代わりに、クロスプラットフォームAPIの安定性を使用する必要がありますか?

+1

なぜあなたは最初に 'static_cast <>'が必要なのですか? – Frank

+1

@Frank 'path :: string()'のオーバーロードが異なるシグネチャを持っているので、それがなければ動作しません。使用する署名を定義する必要があります。 –

+1

これは、ウィンドウ上のパスが2バイトのUTF-16ワイド文字として格納されているためです。:: Linuxへの変換は必須ですが、Linuxではutf-8文字として保存されますか?私は、この特定の変換方法のトレードオフは、いくつかのパス操作が実行されるたびに、ストリングを前後に変換することのトレードオフよりも優れていることを意味しますか? – VTT

答えて

5

古いバージョンのBoost.Filesystemライブラリを使用している可能性があります。 Boost 1.64 says署名は次のとおりです。

string string(const codecvt_type& cvt=codecvt()) const; 

戻り値の型はプラットフォームによって異なります。常に値であり、参照ではありません。これは(ほとんど)the C++17 FileSystem library's definitionと一致することに注意してください。だから、ドキュメンテーションが価値だと言ったときにリファレンスを取得しているなら、そのうちの1つが間違っています。したがって、いずれにしてもバグがあります。

ただし、C++標準(したがって、Boostもそうである)に注意してください。メンバー関数の前提は、文書化された仕様に一致する正確にである必要はないということです。例えば、メンバ関数は、標準にリストされていない追加のデフォルトパラメータを持つことができます。前述のように呼び出し可能である限り、それは有効な実装です。

したがって、std::mem_fnは、のように動作するとは限りません。です。 C++の標準的な言い回しを使用すると、path::stringがその署名を持つメンバポインタに変換できるという前提はありません。そのため、矛盾するかもしれませんが、メンバポインタを取得できるという期待は、Boostのサポートされているインターフェイスではないかもしれません。

それはバグだかどうかは、あなたがラムダを使って、簡単に十分な、これを解決することができます。

std::transform(
    users.begin(), users.end(), std::back_inserter(usersStrs), 
    [](const auto &pth) -> decltype(auto) {return pth.string();}); 

それはstd::mem_fnバージョンよりも、見ている多くのクリーナーです。 decltype(auto)は、参照を返す場合に不要なコピーを防止します。

+0

ラムダのことで、私たちは間違いなく同意します。実際、この定義(codecvt付き)は私のブースト(1.64)で利用可能であり、WindowsとLinux(参照とコピー)の違いもあります...これがバグかどうか疑問です。 Boost 1.64のソースを確認してください。これは 'path.hpp'にあります。 –

+2

@TheQuantumPhysicist:ドキュメントがソースの内容と一致しない場合、そのうちの1つが間違っています。そして、それらのうちのどちらかが間違っているとバグを構成します。また、私の編集を参照してください。 –

+0

ラムダの戻り値型の前に ' - >'がありません。 – aschepler

1

Windowsパスが2バイトのUTF-16ワイド文字として格納されているというコメントのように、std::stringに変換する必要があります。 Boost's path.hppにはWindows APIの次の変換がありますwstringは変換されません。

# ifdef BOOST_WINDOWS_API 
    const std::string string() const 
    { 
     std::string tmp; 
     if (!m_pathname.empty()) 
     path_traits::convert(&*m_pathname.begin(), &*m_pathname.begin()+m_pathname.size(), 
     tmp); 
     return tmp; 
    } 

    // string_type is std::wstring, so there is no conversion 
    const std::wstring& wstring() const { return m_pathname; } 

しかし、LinuxのAPIの変換を、以下、wstringはあなたもthis answerに相談することができ、さらに読書のためにここに

# else // BOOST_POSIX_API 

// string_type is std::string, so there is no conversion 
    const std::string& string() const { return m_pathname; } 

    const std::wstring wstring() const 
    { 
     std::wstring tmp; 
     if (!m_pathname.empty()) 
     path_traits::convert(&*m_pathname.begin(), &*m_pathname.begin()+m_pathname.size(), 
      tmp); 
     return tmp; 
    } 

変換されます。

+0

後者のスニペットはWindowsのmingwとPGIで動作するので、なぜ "windows" Microsoftのバージョンが異なるのでしょうか? – Swift

関連する問題