6

I have a project that is currently locked into Visual Studio 2015. However, I want to write code that is as standards-conforming as possible.

I want to use std::filesystem but it didn't make it into the standard until C++-17. Fortunately, just about everything is available, just in the std::experimental::filesystem::v1 namespace. I'm not a fan of blanket using directives; I prefer to scope things fully to make it clear where the thing comes from. So I'm not going to just put in a global using statement. Some magic is required to convince the compiler to do what I want.

This was my first attempt:

#include <filesystem>
#if defined(_MSC_VER) && _MSC_VER <= 1900 // VS 2015
namespace std {
    namespace filesystem {
        using path = std::experimental::filesystem::v1::path;
    }
}
#endif

That works pretty well, and std::filesystem::path is now accessible. I've tested creating and using path objects and it works.

As I move forward, I know I'm going to need more stuff. I wondered if there might be a way to just bring in the whole thing:

namespace std {
    namespace filesystem {
        using std::experimental::filesystem::v1;
    }
}

That seemed like a step backwards. Nothing appears to be visible. In hindsight, I guess it makes sense, since the scope of the using statement ends with the closing brace on the next line.

Next, I want to get a directory_entry. The same technique seems to work

namespace std {
    namespace filesystem {
        using directory_entry = std::experimental::filesystem::v1::directory_entry;
    }
}

Again, the compiler seems happy.

Now, I want to use std::directory::create_directories. However, this is a function, not a class, so the same technique won't work.

I thought that std::function might be tailor made for this, but I'm not having any luck. I tried

namespace std {
    namespace filesystem {
        function<bool(path)> create_directories = std::experimental::filesystem::v1::create_directories;
    }
}

and the compiler says

Error   C2440   'initializing': cannot convert from 'overloaded-function' to 'std::function<bool (std::filesystem::path)>'

There are two overloads of the function (one takes a second argument to return an error code rather than throwing an exception).

I'm stuck. This has to be possible but my C++-foo is weak.

4

1 回答 1

5

答案在于错误消息,以及std::function为重载方法实例化所需的帮助。感谢MFisherKDX将我指向这里并感谢WF那里的有效答案。

忽略扩展标准命名空间是否合法、道德或良好品味的问题(因为在这种情况下,我相信它至少是 3 个中的 2 个),这是我完整评论的解决方法:

#if defined(_MSC_VER) && _MSC_VER <= 1900
// Visual Studio 2015 work-around ... 
// std::filesystem was incorporated into C++-17 (which is obviously after VS
// 2015 was released). However, Microsoft implemented the draft standard in
// the std::exerimental namespace. To avoid nasty ripple effects when the
// compiler is updated, make it look like the standard here
#include <functional>
namespace std {
  namespace filesystem {
    using directory_entry = std::experimental::filesystem::v1::directory_entry;
    using directory_iterator = std::experimental::filesystem::v1::directory_iterator;
    function<bool(path const&)> create_directories = 
        static_cast<bool(*)(path const&)>(
            std::experimental::filesystem::v1::create_directories);
  }
}
#endif

更新:塞巴斯蒂安有最简单的解决方案。

#if defined(_MSC_VER) && _MSC_VER <= 1900
// Visual Studio 2015 work-around ... 
// std::filesystem was incorporated into C++-17 (which is obviously after VS
// 2015 was released). However, Microsoft implemented the draft standard in
// the std::exerimental namespace. To avoid nasty ripple effects when the
// compiler is updated, make it look like the standard here
namespace std {
  namespace filesystem = experimental::filesystem::v1;
}
#endif

顺便说一句,gcc 7.3 需要几乎完全相同的解决方法,除了你不能

#include <filesystem>

但必须

#include <experimental/filesystem>

反而

于 2018-02-01T23:32:50.533 回答