2

I wanted to do some cross-platform, optional usage of std::filesystem and used code like the below:

#ifdef __has_include
#if __has_include(<version>)
#include <version>
#endif
#endif

#ifdef __cpp_lib_filesystem
#include <filesystem>
// Use of std::filesystem::path
#endif

Then I can pass -std=c++11 or -std=c++17 and have or have not the support for filesystem.

This works fine almost everywhere but on a recent-ish OSX with no explicit target platform level set. This seems to default to some older OSX and throws a compile error:

error: 'path' is unavailable: introduced in macOS 10.15
...
Applications/Xcode_11.3.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/filesystem:739:24:
note: 'path' has been explicitly marked unavailable here

So how am I supposed to handle this on OSX without relying on configure checks if such code compiles? Wasn't the feature detection macro __cpp_lib_filesystem meant to make such configure steps unnecessary?

Flamefire
  • 5,313
  • 3
  • 35
  • 70
  • Did you enable C++17 standard? Also, could this be related to https://stackoverflow.com/questions/58131130/llvm-9-clang-9-osx-stdfilesystempath-unrecognized? – Zaiborg Oct 07 '20 at 08:24
  • @Zaiborg even if not the question of the OP would still be why `__cpp_lib_filesystem` is set if `std::filesystem` is not fully functional. Because [feature testing](https://en.cppreference.com/w/cpp/feature_test) using those defines should allow to e.g. use fallback code. – t.niese Oct 07 '20 at 08:28
  • Yes that question is related. However my code is library code so I cannot add compile flags but have to detect support via the preprocessor. I added a sentence about enabling a given standard to make it clear, the problem comes further down the line. Thanks for the suggestion – Flamefire Oct 07 '20 at 08:37
  • Somewhat related: https://stackoverflow.com/q/64009875/13156261. Including `fstream` seems to avoid to include `filesystem` on OSX. – Eddymage Oct 07 '20 at 10:03

1 Answers1

0

Problem is which version of OSX did you setup to support.

On OSX/MacOS C++ standard library is part of the system. So if you set that your program supports some version of MacOS compiler will do checking if this version of system have standard C++ library which supports specific feature.

So for example it doesn't matter you have enabled C++17, but if your program supposed to support OSX 10.13 this program will not compile:

int foo(std::optional<int> x)
{
   return x.value();
}

Since this can throw an exception: std::bad_optional_access which is part of standard library dynamic library shipped with OS.

On other hand this code will compile just fine:

int foo(std::optional<int> x)
{
   return x.value_or(0);
}

since this version doesn't throw and and templeted code will be generated in your executable only.

Exactly same problem is present with filesystem features. That is why compiler complains you need setup your project to support MacOS 10.15 or newer.

If you are using cmake add this to root CMakeLists.txt:

# note this entry must be set before any target or project is created
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")

If you are using Xcode project: enter image description here

There is a way do detect a platform and make this 10.15 features available. Here is SO answer describing how to do it. This way you can have application which some feature is available only when run on required OS version.

Marek R
  • 32,568
  • 6
  • 55
  • 140
  • That's a really stang behavior by the compiler, shouldn't it set `__cpp_lib_filesystem` to false if the target OS version does not support `filesystem`? Do you know if there is a reason behind that decision? – t.niese Oct 07 '20 at 08:57
  • Compiler supports it, the `Deplyoment target` doesn't so you got error massage. Change `Deplyoment target` to `10.15` and it will compile just fine. – Marek R Oct 07 '20 at 09:03
  • Note also my example, `std::optional` is available, but not all features of it. – Marek R Oct 07 '20 at 09:10
  • IMHO it is still odd, that the toolchain then allows to use c++17 language features for the target but not setting the feature detection defines accordingly, for the set target OS, that defeats somehow the purpose of these. – t.niese Oct 07 '20 at 09:10
  • AFAIK it is possible to add some kind of `if` so compiler will ensure that during runtime you will check platform before using specific feature and the int will compile. This is how it works on Swift. Do not know how this can be achieved in C++. – Marek R Oct 07 '20 at 09:13
  • afaik there is not build in runtime check. Anyhow like the OP I would also have suspected that the toolchain would set the feature detection defines based on the selected c++ version **and** the selected deployment target. Otherwise, these feature detection defines do not make to much sense and is kind of useless. That's why I say that it is an odd behavior of the tool chain. – t.niese Oct 07 '20 at 09:20
  • The whole idea is detect potential compatibility issues. In [swift you can add "if"](https://www.hackingwithswift.com/new-syntax-swift-2-availability-checking) which will check during runtime current platform and scope under this "if" allows to use features available on checked platform bypassing deployment target setting. Since compiler is same there must be similar solution for C++, I never used this in C++ so do not know how to do it. Alternative simplest solutions are: increase deployment target or use boost. – Marek R Oct 07 '20 at 11:01
  • A bit of googling and I've found solution for C++ https://stackoverflow.com/a/57825758/1387438 – Marek R Oct 07 '20 at 11:07
  • Thanks for the answer. To improve it I'd recommend to remove the first part of the answer on elaborating what the issue is and how to set the OSX level because I stated "with no explicit target platform level set", a link to one of the related question with a 1 sentence summary would be fine though as to not distract future readers. What actually counts as the answer is the last paragraph as the question was about the "detection". Problem I now have is: How do I determine if `__builtin_available` is available? – Flamefire Oct 07 '20 at 12:52