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.
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.
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 ).
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.
Visual Studio 2022 includes better cross-platform app development tools and the latest version of C++ build tools, to include C++20 support.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With