Topic category: Library
OS: Windows
IDE: Visual Studio 2022, Community Edition
Context
I recently needed to build the C++ REST SDK [1]. But what used to be a flawless operation has become impossible with the latest versions of Visual Studio 2022 [2] without a manual intervention. Fortunately, the issue happened to be easy to overcome.
Issue detailed description
The C++ REST SDK turned out to build just fine in Release configuration, but did not anymore in Debug one with an up-to-date Visual Studio 2022. The compilation failed because of a C4996 warning treated as an error:
...\include\cpprest\containerstream.h(404,47): warning C4996: 'stdext::checked_array_iterator<char *>': warning STL4043: stdext::checked_array_iterator, stdext::unchecked_array_iterator, and related factory functions are non-Standard extensions and will be removed in the future. std::span (since C++20) and gsl::span can be used instead. You can define _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to suppress this warning.
And no fix is to be expected as the project has been halted.
Considering the possible solutions
Actually, several solutions were suggested by the warning message itself: std::span (since C++20) and gsl::span can be used instead
and You can define _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to suppress this warning.
.
It would indeed be easy to suppress the warning, but this should be considered as a last resort if no safer solution can be found. Or, since the library builds successfully in Release configuration, the code specific to this configuration could be made generic, but it should be avoided for the same reason as the stdext::checked_array_iterator object [3] purpose is to ensure memory is not read or written out of bounds. The safest solution would be to replace the stdext::checked_array_iterator object with the std::span [4] class one. But can it be done easily? And does it bring an equivalent level of safety to the code? I decided to make sure of it.
Verifying the std::span object safety
I was able to verify the std::span object safety with just a few lines of code.
- I started with creating a simple project to host my code. A mere console application [5] was perfect for my needs.
-
Then I came up with the following code:
#include <array> #include <iostream> #include <span> using namespace std; int main() { constexpr const array<int, 5> array{ 0, 1, 2, 3, 4 }; // We create a span over the array, purposely excluding its extremities const span<const int> span{ array.cbegin() + 1, array.size() - 2 }; // We attempt to access an array element preceding the span cout << "Before span beginning: " << *(span.begin() - 1) << endl; // We attempt to access an array element following the span cout << "Span end: " << *span.end() << endl; return 0; } -
I tried to build it, but the compilation failed. It was expected though, because the version of C++ used by default in C++ projects is C++ 14, and the std::span object has been introduced in C++ 20. Setting the dedicated compilation option to the appropriate value [6] solved the problem.
-
I executed the code in Release configuration to validate its behavior. As I expected, it completed successfully and produced the following output:
Before span beginning: 0
Span end: 4 -
Then I executed it in Debug configuration. Since I was using the span object to access memory not belonging to it, I expected the execution to break and an error message to display, just as it does with other STL containers in similar circumstances. And this is exactly what happened:
-
I just verified std::span safely detects accesses to memory before its beginning. Even if I had little doubt about it, I ensured it was the same for memory after its end by commenting out the breaking line (#14), rebuilding the project and executing it again in Debug configuration.
// We attempt to access an array element preceding the span // cout << "Before span beginning: " << *(span.begin() - 1) << endl; // We attempt to access an array element following the span cout << "Span end: " << *span.end() << endl; return 0; }I was proved right, as I met a similar behavior:
Fixing the C++ REST SDK build
All I had to do now was to replace stdext::checked_array_iterator with std::span. It took only two modifications in the file failing to compile, "containerstream.h" [7].
-
Before all, I included the std::span header somewhere in the file inclusion area:
... #include <iterator> #include <queue> #include <vector> #include <span> namespace Concurrency { namespace streams { ... -
And I performed the replacement itself:
... #if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0 // Avoid warning C4996: Use checked iterators under SECURE_SCL std::copy(readBegin, readEnd, std::span<_CharType>{ ptr, count }.begin()); #else std::copy(readBegin, readEnd, ptr); #endif // _WIN32 ...
And that was it. My next attempt to build the library in Debug configuration did not meet any additional error and completed successfully.
Conclusion
It did not took me more than a short search to solve my build issue while being able to preserve the safety of the code to build, and the only requirement I had to follow for this was to use C++ 20. Additionally, it gave me the opportunity to discover the std::span object, which interests me greatly and which I cannot wait to put at use.
References
[1] GitHub: The C++ REST SDK[2] GitHub: The C++ REST SDK, issue #1768: stdext::checked_array_iterator compilation error with VS 2022 17.8 Preview 2
[3] Microsoft Learn: The checked_array_iterator class
[4] cppreference.com: std::span
[5] Microsoft Learn: Create a C++ console app project
[6] Microsoft Learn: /std (Specify Language Standard Version): To set this compiler option in the Visual Studio development environment
[7] GitHub: The C++ REST SDK, source code: Release/include/cpprest/containerstream.h