-
Notifications
You must be signed in to change notification settings - Fork 0
Home
A recreation of much of the modern C++ STL in C++98; requiring no language extensions, compiler features, or C++0x and up support. Obviously this cannot replicate core language features such as move semantics; however a surprising amount of the modern standard library is perfectly implementable even as far back as C++98.
The interface has been designed to make switching up to a modern C++ standard as seamless as possible. While I would recommend exercising due care and diligence with your code, in most situations it can be achieved simply by replacing #include directives and finding-and-replacing dp:: with std::, such that a code block which looks like this today
#include "cpp98/optional.h"
dp::optional<Data_Type> get_data(){
if(data_can_be_obtained) return obtain_data();
else return dp::nullopt;
}Can be simply upgraded upon reaching C++17 to
#include <optional>
std::optional<Data_Type> get_data(){
if(data_can_be_obtained) return obtain_data();
else return std::nullopt;
}Note I make no promise of ABI compatibility between my tools here and their standard equivalents.
This is a header-only library. All the code you need is included in the cpp98 subdirectory of the include directory. The bits subdirectory is for files which are used in the internal workings of the library. They are not intended to be a part of the public interface, and I do not recommend using them in your projects.
The headers included in this library are:
-
algorithm - Standard library algorithms added in C++11 and onwards, which would normally live in the
<algorithm>header. -
array - An analogue of
std::array, sharing the same functionality and interface. -
expected - An analogue of
std::expected- A type which contains either an expected value or an error value. -
numeric - A light implementation of some of the functions added to the
<numeric>header from C++11 and up. Does not include algorithms added for parallelisation purposes as C++98 has no standard concurrency primitives. -
optional - An analogue of
std::optional. -
range_functions - Contains analogues of the C++11-C++17 "range access" functions :
std::begin();std::end(); c-, r-, and cr- versions of the same;std::size(),std::ssize(),std::empty(), andstd::data(). This also adds some generic iterator types to complement use ofdp::begin()etc in a generic way; which can be safely replaced byautoon a future standard. -
ratio - A recreation of the standard
<ratio>header, for compile time rational arithmetic. - rc_base - A general-purpose base class for creating reference counted, COW classes. Intended for use in rc_ptr as well as other classes which seek to add ref-counting, COW behaviour.
- rc_ptr - A reference-counted copy-on-write smart pointer.
-
scoped_ptr - A unique-owning, scope-local smart pointer. An analogue of
std::unique_ptr, but of course without move semantics. -
static_assert - A basic but effective replacement for the
static_assertkeyword. - type_traits - A recreation of many of the standard type traits from the standard type_traits header.
-
utility - A recreation of the modern features in the
<utility>header.
While the interface of these headers and tool should match their standard library counterparts closely, individual pages are available to summarise the core features
-
Why do you not have X from Y header? When adding a new feature or header I strive to include every part of that header in modern code which I reasonably can. Many library features in modern C++ rely on modern core language features or compiler magic, and so these are not included in the repo. There are also many features in the modern C++ standard library which are perfectly possible to implement in C++98, and these are what I include.
-
Why do some names match their standard library counterpart (e.g. array) and others don't (e.g. scoped_ptr)? The distinction there is simple - constructs which match the contract of the standard library object (within the confines of C++98) share the name. Constructs which are lacking meaningful parts of their standard library counterpart do not. For example -
scoped_ptrdoes not have any move semantics associated with it. It can't, we're in C++98. But move semantics are a large part of what makesstd::unique_ptrwhat it is; and a moveable unique-ownership pointer is meaningfully different from a scope-local, unmoveablescoped_ptr. On the other hand,arraymatches the interface and requirements ofstd::arraymore exactly; and while it too is missing move semantics, they are a significantly less important part of what makesstd::arraywhat it is, so the name is the same. -
What about range_functions and similar "collection" headers? The C++ standard library often mandates that some functions be included in several different headers. Take the range functions for example - as of C++23,
std::beginis specified to appear in 14 different standard library headers, and none of them exclusively hold those functions. It seemed better to give such headers their own names rather than to use a name of a header which is irrelevant to the contents of the file, or a name which promises more features than I was ultimately able to include.