Skip to content

Notes on updating

DryPerspective edited this page Oct 10, 2023 · 8 revisions

I would hope that if you find yourself still writing code in C++98, it is with the goal of eventually updating to a modern language standard. As such, you will likely find yourself in the position where you want to move away from this library and embrace the standard library tools. This is something you should do if you find yourself in C++11 or higher, as while this is a perfectly adequate library for C++98, the core language features added in C++11 and higher (constexpr, auto, and move semantics to name a few) mean that the standard library versions will outperform this library every time.

The macros DP_CPPXX, where XX is a number corresponding to a language standard, exist to aid in this process. When defined, they will selectively disable any library features which rely on some part of the standard which were removed between C++98 and that language standard. This was done to prevent some unrelated part of the library causing compilation to fail during the update process, so that e.g. you could test that you'd correctly swapped out dp::optional for std::optional without the fact that the smart pointers have a constructor for std::auto_ptr from preventing compilation and causing an unrelated mess which you would have to clean up.

This library was designed to mimic the interface of the standard closely, so for the most part updating is simple - change #include cpp98/foo.h to #include <foo>; and change dp::bar<...>(...); to std::bar<...>(...) and you should be good to go (though as always I strongly recommend appplying this change carefully and not just running a find-and-replace and calling it a day). However, there are one or two places in the library where I break with the standard interface because it is not possible to maintain it in C++98 and not cause some form of conflict. These cases are listed in the table below:

This Library Standard Equivalent
dp::scoped_ptr<T> std::unique_ptr<T>
#include "cpp98/scoped_ptr.h #include <memory>
#include "cpp98/shared_ptr.h #include <memory>
#include "cpp98/null_ptr.h" No include needed for nullptr.
#include <cstddef> needed for std::nullptr_t
dp::null_ptr
dp::null_ptr_t
nullptr
std::nullptr_t
#include "cpp98/static_assert.h" No include needed
dp::static_assert_98<condition>()
STATIC_ASSERT(condition)
static_assert(condition)
dp::unexpect(...) std::unexpected(...)
dp::cow_ptr<T> No standard version exists
#include "cpp98/reference_wrapper.h" #include <functional>
#include "cpp98/byte.h #include <cstddef>
dp::iterator_type<T>
dp::const_iterator_type<T>
dp::reverse_iterator_type<T>
dp::const_reverse_iterator_type<T>
Likely not needed as formally spelled types. Most uses can be replaced by auto

Note of course that the Borland types are not standard C++ so will not have standard equivalents. I recommend writing your own versions of these for the updated language standard if needed.

Features per standard

The features of this library cover a wide range of modern C++ language standards, from C++11 to C++23. To aid with the update process, I'll provide a broad-strokes list of the language standards from which each major feature of the library are adapted. This will not list, e.g., every algorithm from <algorithm> but will provide a good starting point for the larger and more commonly used types and allow you to see where you may need to find alternatives:

C++11:

  • dp::scoped_ptr/std::unique_ptr
  • shared_ptr
  • <array>
  • get_new_handler
  • nullptr and std::nullptr_t
  • <ratio>
  • reference_wrapper
  • static_assert
  • All functions in cpp98/string.h except erase/erase_if added to <string>
  • <typeindex>
  • <type_traits>

C++17:

  • <any>
  • std::byte
  • <optional>
  • <string_view>

C++20:

  • <bit>
  • <span>
  • std::erase & std::erase_if for std::string and std::vector

C++23:

  • <expected>

Note that alternatives exist and can be found, or hand-written. For example, gsl::span provides a version of <span> which is compatible with earlier language standards (and indeed was what std::span was adapted from); and std::erase simply formalises the erase-remove idiom which can be easily rewritten as needed.

Clone this wiki locally