Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I make std::filesystem appear standards-conforming to Visual Studio 2015

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.

like image 535
jwm Avatar asked Feb 01 '18 22:02

jwm


People also ask

How do I change the C++ language standard in Visual Studio?

Select the Configuration Properties > C/C++ > Language property page. In C++ Language Standard (or for C, C Language Standard), choose the language standard to support from the dropdown control, then choose OK or Apply to save your changes.

Does Visual Studio support C ++ 11?

Support for C11 and C17 standards is available in Visual Studio 2019 version 16.8 and later. Support requires an updated Universal C Runtime (UCRT) and Windows SDK version to work properly with the conforming preprocessor ( /Zc:preprocessor ).

What version of C++ is Visual Studio using?

The compiler now supports the /std:c++20 compiler mode. Previously, C++20 features were available only in /std:c++latest mode in Visual Studio 2019. Features that originally required /std:c++latest mode now work in /std:c++20 mode or later in the latest versions of Visual Studio.

What version of C++ does Visual Studio 2022 use?

Visual Studio 2022 includes better cross-platform app development tools and the latest version of C++ build tools, to include C++20 support.


1 Answers

The answer lies in the error message, and the help required to instantiate std::function for an overloaded method. Thanks to MFisherKDX for pointing me here and to W.F. for the answer there that works.

Ignoring the question of whether it is legal, moral, or in good taste to extend the standard namespace (because in this case I believe it is at least 2 of the 3), here's my fully commented work-around:

#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

UPDATE: Sebastian had the simplest solution.

#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

By the way, gcc 7.3 requires nearly exactly the same work-around, except you can't

#include <filesystem>

but must

#include <experimental/filesystem>

instead

like image 114
jwm Avatar answered Oct 23 '22 03:10

jwm