How to selectively include boost headers depending on boost version - c++

This change in Boost caused some header file locations to change: https://github.com/boostorg/property_tree/commit/ea940990691de91e9b22255d9b450fcdac237646
I am working on a codebase where some users build with an older version of boost, with the old header locations, e.g. #include <boost/property_tree/detail/json_parser_error.hpp> instead of the newer #include <boost/property_tree/json_parser/error.hpp>. I want to retain compatibility with both older (pre-1.61) and newer boosts.
Is there a way to instruct the compiler to check for the boost version and use the new header include if the version >= 1.61?

I think you are looking for BOOST_VERSION preprocessor. It is defined in boost\version.hpp
// Caution, this is the only boost header that is guarenteed
// to change with every boost release, including this header
// will cause a recompile every time a new boost version is
// released.
//
// BOOST_VERSION % 100 is the patch level
// BOOST_VERSION / 100 % 1000 is the minor version
// BOOST_VERSION / 100000 is the major version
Simple usage would be:
#if BOOST_VERSION >= 106100
#include <this.hpp>
#else
#include <that.hpp>
#endif

Related

How to determine whether to use <filesystem> or <experimental/filesystem>?

Is there a way to determine whether I can use the standard <filesystem> (which is available on all modern C++ compilers that support C++17) or <experimental/filesystem> which is used by older compilers. (For example g++ 6.3, which is the current standard version on Debian Stretch)
Knowing which one to use is imporant because the first uses std::filesystem::xxx and the latter std::experimental::filesystem::xxx.
I typically create a header filesystem.hpp with the following content:
// We haven't checked which filesystem to include yet
#ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
// Check for feature test macro for <filesystem>
# if defined(__cpp_lib_filesystem)
# define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
// Check for feature test macro for <experimental/filesystem>
# elif defined(__cpp_lib_experimental_filesystem)
# define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
// We can't check if headers exist...
// Let's assume experimental to be safe
# elif !defined(__has_include)
# define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
// Check if the header "<filesystem>" exists
# elif __has_include(<filesystem>)
// If we're compiling on Visual Studio and are not compiling with C++17, we need to use experimental
# ifdef _MSC_VER
// Check and include header that defines "_HAS_CXX17"
# if __has_include(<yvals_core.h>)
# include <yvals_core.h>
// Check for enabled C++17 support
# if defined(_HAS_CXX17) && _HAS_CXX17
// We're using C++17, so let's use the normal version
# define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
# endif
# endif
// If the marco isn't defined yet, that means any of the other VS specific checks failed, so we need to use experimental
# ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
# define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
# endif
// Not on Visual Studio. Let's use the normal version
# else // #ifdef _MSC_VER
# define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
# endif
// Check if the header "<filesystem>" exists
# elif __has_include(<experimental/filesystem>)
# define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
// Fail if neither header is available with a nice error message
# else
# error Could not find system header "<filesystem>" or "<experimental/filesystem>"
# endif
// We priously determined that we need the exprimental version
# if INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
// Include it
# include <experimental/filesystem>
// We need the alias from std::experimental::filesystem to std::filesystem
namespace std {
namespace filesystem = experimental::filesystem;
}
// We have a decent compiler and can use the normal version
# else
// Include it
# include <filesystem>
# endif
#endif // #ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
It even creates an alias for std::experimental::filesystem to std::filesystem if the experimental headers are used.
Which means you can simply include this header in place of <filesystem>, use std::filesystem::xxx and enjoy support from older compilers too.
A few notes on the details of this snippet:
__cpp_lib_filesystem and __cpp_lib_experimental_filesystem
These are Feature Testing Macros. They should be available when the respecitive headers are available. But VisualStudio 2015 (and below) don't support them. So the rest is just to make sure we can make an accurate assesment, instead of relying on unreliable macros.
__has_include()
While most compilers do have that macro built in, there is no gurantee, as it is not in the standard. My snippet checks for it's existence before it is used. And in case it doesn't exist, we assume we have to use the experimental version to provide maximum compatibility.
defined(_MSC_VER) && !(defined(_HAS_CXX17) && _HAS_CXX17)
Some versions of VisualStudio (namely 2015) have just an half arsed implementation of C++17. And it's possible that the <filesystem> header exists, but std::filesystem doesn't. This line checks for that case and uses the experimental version instead.
#error ...
If the header check is available and we can't find either header we just print a nice error, as there's nothing we can do.
INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
You even get a marco that let's you know which version is in usage so you could write pre processor statements of your own that deal with the differences between the versions.
namespace filesystem = experimental::filesystem;
This alias definition is just for convinice that'll make sure that you'll have std::filesystem, assuming your compiler let's you do it (I haven't seen a single one that doesn't allow that).
According to the standard defining anything in the std namespace is undefined behavior. So if your compiler, concience, colleguages, code standard or whatever complains, just define namespace fs = std::experimental::filesystem; in the upper block and namespace fs = std::filesystem; in the lower. (Just to be sure, if you do that, remove the namespace std { stuff)
P.S.: I created the answer and this question, because I spent an awful lot of time getting frustrated with older compilers not having the <filesystem> header. After a fair amount of research and testing on multiple platforms with multiple compilers and versions of them, I managed to come up with this universal solution. I have tested it with VisualStudio, g++ and clang (Only with versions that actually do have at least experimental support for C++17).
Should there be an issue with another compiler, let me know and I'll make it work for it too.
I typically use the feature test macros a lot for this type of problem. I am currently faced with this exact problem but I have used __cpp_lib_filesystem along with the using keyword.
// since C++ 20
#include <version>
#ifdef __cpp_lib_filesystem
#include <filesystem>
using fs = std::filesystem;
#elif __cpp_lib_experimental_filesystem
#include <experimental/filesystem>
using fs = std::experimental::filesystem;
#else
#error "no filesystem support ='("
#endif
I am using this on gcc-6 and up as well as clang-6, sadly no older copy of studio to test against but it works on 15.7 and up.

Intel TBB - 'InitializeCriticalSectionEx': identifier not found compiler error

I have a VS (C++) project that relies on OpenCV and TBB, so I created property sheets for each library and included them in the project. Everything worked fine and the code compiled.
Yesterday, I have started using vcpkg package manager. I installed OpenCV and TBB via vcpkg and everything seemed to work. I created an empty project, included the headers of both and tested if the new compiled libraries work. After verifying that, I went back to my main project and removed the property sheets, so I can use the libraries from vcpkg. I did not change the code in any way since the last successful compilation.
But when I try to compile the code now I get this error two times (in main.cpp and in a submodule)
tbb\critical_section.h(53): error C3861: 'InitializeCriticalSectionEx': identifier not found
Does anybody know what is going on here or why this error occurs?
Update
I found the error myself. I'm adding the poco-libraries tag, because it's actually a conflict between TBB and Poco.
I found the source of the problem and it has actually nothing to do with TBB but with the Poco library.
Consider the minimum example:
#include <Poco/Poco.h>
#include <tbb/tbb.h>
void main()
{
}
This will throw an compiler error.
Tracing down the path
When including tbb.h, critical_section.h is included in line 51 of tbb.h. However, ciritcal_section.hpp includes machine/winwdows_api.h which looks like this (unnecessary stuff is cut out):
tbb/machine/winwdows_api.h:
#if _WIN32 || _WIN64
#include <windows.h>
#if _WIN32_WINNT < 0x0600
#define InitializeCriticalSectionEx inlineInitializeCriticalSectionEx
inline BOOL WINAPI inlineInitializeCriticalSectionEx( LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD )
{
return InitializeCriticalSectionAndSpinCount( lpCriticalSection, dwSpinCount );
}
#endif
As you can see, windows.h is included before the check of the _WIN32_WINNT macro. This macro is defined in sdkddkver.h (which is included in windows.h), iff it's not already defined (in my case it's set to Win10):
sdkddkver.h:
#if !defined(_WIN32_WINNT) && !defined(_CHICAGO_)
#define _WIN32_WINNT 0x0A00
#endif
In windows.h, the _WIN32_WINNT macro controls which version of the windows header files are actually included. If _WIN32_WINNT is set to an earlier version than Windows Vista, the function InitializeCriticalSectionEx is not defined.
This issue is catched by machine/winwdows_api.h (as you can see in the code block of that file) by simply defining a macro InitializeCriticalSectionEx that calls an appropriate alternative function.
So far so good.
The problem
The root of all evil lies in Poco/UnWindows.h of the Poco library. When including a poco header, at some point UnWindows.h will be included.
Poco/UnWindows.h (shortened):
#if defined(_WIN32_WINNT)
#if (_WIN32_WINNT < 0x0501)
#error Unsupported Windows version.
#endif
#elif defined(NTDDI_VERSION)
#if (NTDDI_VERSION < 0x05010100)
#error Unsupported Windows version.
#endif
#elif !defined(_WIN32_WINNT)
#define _WIN32_WINNT 0x0501
#define NTDDI_VERSION 0x05010100
#endif
#endif
#include <windows.h>
The preprocessor checks, if _WIN32_WINNT is already defined, and if not, sets it to 0x0501 which is Windows XP. After that, windows.h is included. In the previous chapter I mentioned that _WIN32_WINNT controls which version of the windows header files are actually included.
Now imagine, the very first include in our project is a header from Poco. This means, that _WIN32_WINNT will be set to Windows XP and windows.h will include the windows headers of Windows XP (which imo is already a bad sign).
But don't worry, it gets worse.
If we trace the include hierarchy one level up, we reach Poco/Platform_WIN32.h.
Poco/Platform_WIN32.h (shortened):
#include "Poco/UnWindows.h"
...
#if defined (_WIN32_WINNT_WINBLUE)
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT _WIN32_WINNT_WINBLUE
...
Funny, isn't it? First, it includes UnWindows.h, which sets _WIN32_WINNT and causes Windows XP headers to be included, and next it redefines _WIN32_WINNT to be Windows 8.1. I have no clue why it does that, maybe there is a good reason, idk.
If we now look at the minimum example at the very top we see that Poco is included before TBB. What now happens is:
Include Poco headers
Set _WIN32_WINNT to Windows XP
Include windows headers (Windows XP version, because of 2)
Reset _WIN32_WINNT to Windows 8.1
Include TBB headers (windows headers are already included, so TBB doesn't need to include them again in tbb/windows_api.h)
TBB checks the windows version via _WIN32_WINNT and recognizes Windows 8.1 (as set by Poco)
TBB thinks InitializeCriticalSectionEx is defined, because the Windows version is 8.1 (or is it? Poco says: get rekt) and InitializeCriticalSectionEx is defined since Windows Vista.
Unfortunately Poco ensured that the Windows XP headers are loaded, so compiler says: no.
The solution
Either include windows.h yourself beforehand, or set _WIN32_WINNT yourself beforehand:
#define _WIN32_WINNT 0x0A00 // either this
#include <Windows.h> // or this
#include <Poco/Poco.h>
#include <tbb/tbb.h>
void main()
{
}
Maybe someone of the Poco contributors can clarify some things here. The Poco version is 1.8.1-1 built with x64 (via vcpkg).
Update
Poco is on the issue. Updates can be found here.

Force boost to use POSIX shared memory instead of System V?

Boost by default is using System V shared memory and creating file for communication. I want it to use POSIX shared memory.
Is their a way to force boost to use POSIX shared memory instead of trying to create a file?
Do I have to change something in source code (Boost.Interprocess)?
I am using 1.46.1 version of boost in my project on OS X (10.6, 10.7 and 10.8). But I can use 1.52.0 too. Before to migrate I want to know if it's even possible to do.
From the documentation, Boost Interprocess will use POSIX when available. Digging further into it, it appears that on Max OSX it does not because the implementation has some non-conforming behavior (according to the header) . Below is the code from boost/interprocess/detail/workaround.hpp.
//Check for XSI shared memory objects. They are available in nearly all UNIX platforms
#if !defined(__QNXNTO__)
#define BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS
#endif
#if defined(_POSIX_SHARED_MEMORY_OBJECTS) && ((_POSIX_SHARED_MEMORY_OBJECTS - 0) > 0)
#define BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS
#else
//VMS and MACOS don't define it but they have shm_open/close interface
#if defined(__vms)
#if __CRTL_VER >= 70200000
#define BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS
#endif
//Mac OS has some non-conformant features like names limited to SHM_NAME_MAX
#elif defined (__APPLE__)
//#define BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS
//#define BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS_NO_GROW
#endif
#endif

boost::TIME_UTC(_) with different boost versions

I just upgraded my project on Windows from boost 1.46 to the current boost 1.52. We have some usages of boost::TIME_UTC which I changed to boost::TIME_UTC_ according to https://svn.boost.org/trac/boost/ticket/6940.
However, we also build the source on some linux machines with boost 1.49 which doesn't know the boost::TIME_UTC_. Is there any suggested way to use boost 1.49 and 1.52 in parallel with TIME_UTC?
Change everything to TIME_UTC_. Then use this:
#include <boost/version.hpp>
#if BOOST_VERSION < 105000
#define TIME_UTC_ TIME_UTC
#endif
We use:
#include <boost/version.hpp>
#if BOOST_VERSION < 105000
#include <boost/thread/xtime.hpp>
namespace boost {
enum xtime_compat {
TIME_UTC_=TIME_UTC
};
}
#endif
This way you can use boost::TIME_UTC_, as in 1.50 onwards.
But not for openSuse, because they decided to merge this change back to 1.49.

Automatically Including OpenCV Libraries of Different Versions

I've been automatically including the opencv library files for my c++ code on Visual Studio 2008 on Windows 7 with the following code:
#ifndef NDEBUG
#pragma comment(lib, "opencv_core231d.lib")
#pragma comment(lib, "opencv_highgui231d.lib")
#pragma comment(lib, "opencv_imgproc231d.lib")
#else
#pragma comment(lib, "opencv_core231.lib")
#pragma comment(lib, "opencv_highgui231.lib")
#pragma comment(lib, "opencv_imgproc231.lib")
#endif
but I run into trouble when the system has a different version of the opencv library installed because the .lib files have the version (in this case, 2.31) in the filename. Is there a good way to automatically or near-automatically detect what version of the opencv library is available then slide in the appropriate version string into the pragma?
There are small changes between different OpenCV versions - not much, but enough to crash your app when you change from 2.0 to 2.2 or from 2.2 to 2.3.1. Also, the beta 2.4 release has enough changes from previous ones.
So best is to test your app with an OpenCV version, and deliver it with those dlls only.
A small example:
Mat a(3,3,CV_8UC3);
a.setTo( Scalar(10) ); // in 2.3.1 will set all channels to 10,
// in 2.2 will only set first channel.
The corresponding 2.2 call would be
a.setTo(Scalar::all(10));
Or
a = 0; // runs fine on 2.3.1. Equivalent to setTo().
// Does not compile on earlier versions
Another example is cv::drawPoly(), which has a different signature on 2.2 and 2.3.1.
Given the fact that those changes are not well documented, the chance to miss one of them by mistake is really high.
I have worked out a solution to having code run on various versions of OPENCV by merging the following articles:
How to make a string out of macros
Concatenating strings in macros
Pragma statements in #defines with support for VS2008 here on MSDN
Bonus: How to have global settings defined in a solution
As noted, this can cause some serious grief if you pop between incompatible versions but for those versions and features supported this may be useful to some. Use this technique to set the opencv version once and have your code automatically link to the desired version by either
define the OPENCV_VERSIONin one place in your source code
Define it in a Property Sheet
Define it in a system environment variable.
My code ended up like so:
#include <iostream>
// #define OPENCV_VERSION $(OPENCV_VERSION)
// #define OPENCV_VERSION 220
#define QUOTE(name) #name
#define STR(macro) QUOTE(macro)
#define LINK_TO_OPENCV(libname) __pragma(comment(lib, "opencv_" #libname STR(OPENCV_VERSION)))
#define LINK_TO_OPENCV_DEBUG(libname) __pragma(comment(lib, "opencv_" #libname STR(OPENCV_VERSION) "d"))
#ifndef NDEBUG
LINK_TO_OPENCV_DEBUG("core")
LINK_TO_OPENCV_DEBUG("highgui")
LINK_TO_OPENCV_DEBUG("imgproc")
#else
LINK_TO_OPENCV("core")
LINK_TO_OPENCV("highgui")
LINK_TO_OPENCV("imgproc")
#endif
int main()
{
std::cout << STR(LINK_TO_OPENCV("core")) << "\n";
return 0;
}
And now to set the OPENCV_VERSION anywhere you like. For example, you can have a single header file included by everyone that has the line:
#define OPENCV_VERSION 220
Or you can goto Project->Properties->C/C++->Preprocessor and set Preprocessor Definitions to OPENCV_VERSION=220. Or you can do the same thing in a shared property sheet for the entire solution.
Or, and this is important, you can use this technique to define a global environment variable in windows itself called OPENCV_VERSION_ENV and set its value to the version code (say, 220). Then you can put set the preprocessor definition to OPENCV_VERSION=$(OPENCV_VERSION_ENV) and you will bring in the environment variable into the link command. You CANNOT do a #define OPEN_VERSION $(OPENCV_VERSION_ENV) but since the property pages will automatically translate $(macros) we can get environment variables there.