rcolyer.net
RC Lib  Version 202403231100
RC Lib

Download

You can download the latest version of RC Lib as a zip or tar.xz. (Or you can obtain older versions.)

This documentation can also be downloaded as a pdf.

Introduction

RC Lib is a cross-platform C++ programming library designed to significantly improve the way you design and structure programs. It seems to be generally accepted that one must choose a compromise between runtime performance and syntactical convenience for the programmer. This library is designed with the philosophy that this compromise is not necessary. After programming for quite some time, I found that my programming tasks largely settled into two categories. If I needed something high performance or with a complicated architecture, I would write it in C++. And if I wanted to quickly prototype an idea, or quickly parse some files or text, I would write in a higher level language like perl. When a problem fell in the uncomfortable middle ground, neither approach felt quite right.

RC Lib seeks to provide a seamless integration of these approaches by providing high level language features for parsing text, working with files, and structuring data, while preserving the structural advantages and characteristic performance of C++. Whenever possible syntactic sugar for usage of the library was favored over the simplicity of the implementation.

A core design goal of RC Lib is the ease of writing robustly correct and secure programs. Where a bound can be checked, it is checked. Where a pointer can be verified as non-NULL, it is verified. The idea that libraries should avoid validating inputs in the name of efficiency, as is done throughout the stdlib, is an antiquated notion which has resulted in a great deal of harm. It is the responsibility of the compiler to optimize away input validation when it can be determined to not be necessary, and in fact modern compilers often do an excellent job of this. With branch prediction in modern cpu's, the impact of remaining validation is negligible. With this in mind, RC Lib is designed from the ground up to favor ease of writing correct code.

RC Lib primarily targets cross-platform Linux and Windows development, although presumably it would also compile on OS X with minimal problems. (If anyone wishes to be responsible for OS X testing, please contact me.)

Lastly, RC Lib is provided freely and with the permissive Boost license, to remove barriers to its use.

File handling

There is a fundamental philosophical difference between the stdlib string and stream classes and the RC Lib string and file classes. The stdlib performs character formatting and parsing at the input and output classes, while the string class handles only basic storage and character manipulation. While the stdlib streams provide an overload interface with "<<" and ">>" for serialized character data, for binary data the stream read/write interface works only with a byte array. In RC Lib, the responsibility for formatting of character data and conversion of other types to character data is centralized in the string class (RC::RStr), while the file classes (RC::FileRead, RC::FileWrite, and RC::FileRW) use overloading of Get and Put to process binary data of various plain old data types as well as complex classes.

String manipulation

The string manipulation features of RC Lib are centralized in RC::RStr, which wraps and augments std::string. RStr has constructors for converting all of the primitives to strings, along with all of the formatting options for base and precision. It also has a series of Get calls for parsing out primitives. For aligning and formatting text there are a series of Pad and Wrap functions. RStr also has syntactically elegant regular expression matching and substitution. (These wrap the C++11 implementations, or for pre-C++11 can make use of the Boost regular expression libraries.)

RStr also provides high level capabilities like splitting and joining strings. For example SplitWords will return a one dimensional structure of all the words. Also some more specialized routines are available for working with comma-separated values, base64 encoding, and long hex strings.

An additional advantage of keeping string manipulation in the string class rather than in streams arises with multithreaded programming, in which concurrent stream output calls can be interwoven. Thus it is prudent to consistently prepare the entire formatted string within the originating thread, and then output it atomically.

Error handling

A common infrastructure for error handling is used throughout RC Lib of throwing RC::ErrorMsg exceptions, or sub-types like RC::ErrorMsgFile. The ErrorMsg class provides a cross-platform way to achieve a stack trace for any unexpected errors. If an ErrorMsg is allowed to propagate through to program termination, this will result in an informative stack trace of the error being printed to stderr, facilitating debugging. Alternatively, programs can capture and display or log this stack trace.

With traditional development protocols there is often a huge disconnect between users in the field experiencing bugs, and developers in a debugger attempting to reproduce them. Bugs that require specific conditions can often go years without being fixed. The RC::ErrorMsg approach attempts to resolve that by assisting users in providing debugging-useful error messages when unhandled exceptional events occur.

Smart pointers

There are three approaches to smart pointers in RC Lib. The basic pointer class, RC::Ptr, operates as a straight templated substitute for the classic C pointer, but ensures the pointer defaults to NULL, and provides a debuggable ErrorMsgNull exception if a NULL dereference is accidentally attempted. This should be used liberally wherever a C pointer would otherwise be used, since RC::Ptr is easily optimized away when safe to do so, and it casts both implicitly and explicitly to a C pointer.

Automatic memory management is provided by RC::APtr, which is a reference counting pointer that deletes the object automatically when the last copy of APtr leaves scope. The reference counted pointer is implicitly shared every time an APtr is assigned or copy-constructed from another APtr (in a thread-safe manner with C++11). Any place you find a manual delete call in your code, try replacing it with a properly scoped APtr to make all of your memory management automatic.

Self-referential pointers can be managed with RC::RevPtr, which is a shared pointer that can be made to "revoke" itself, or make all shared copies NULL, when the object containing the master copy leaves scope. In this way a pointer can be safely obtained to an object which may be deleted, such that the pointer will become NULL when the object no longer exists. Use this to immunize against dangling pointers.

Data structures

For basic array storage, RC::Data1D, RC::Data2D, and RC::Data3D provide features comparable to std::vector, but with built-in bounds checking, more efficient memory management in numerous areas, and the ability to access and extract a raw C array corresponding to the managed storage. The Data#D containers each contain lightweight resizing and offset operations, meaning they can be made to temporarily refer only to portions of the data. When an array must be generated for working with a C library, the Raw and Extract methods permit interfacing these rather than falling back to problem-prone C-style memory management. Conversely Data1D can be made to wrap an externally obtained C-style array, providing bounds checking and the other features. In tight loops, the bounds checking of these structures is often optimized away.

Similar features are provided by the RC::Bitfield, RC::Bitfield2D, and RC::Bitfield3D classes, but for efficiently packed arrays of bits. In contrast to std::vector's specialization of bool, Data1D<bool> has underlying storage of precisely what it says, an array of bools.

The RC::Tuple class provides features analogous to std::tuple, but with the added ability to Apply the Tuple contents as parameters to a function call, cast them all to a fixed type Data1D, or implicitly construct them during a function return. For example, Tuple<int, RStr> f() { return {3, "foo"}; }

Syntactic sugar

Short-hand consistent types are provided in Types.h of i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, and when available, f80 and f128. Those beginning with i are signed integers, with u unsigned integers, and with f floating point types, while the number following is of course the bit size. This provides a more predictable cross-platform sizing than the default types in a uniform and syntactically pleasing shorthand.

Conditional comparisons are made more elegant by the helper methods RC::Betw and RC::OneOf. Rather than "if (3.14 < x && x <= 9)" one can write the more natural "if (3.14 < Betw(x) <= 9)", which remains valid for any type combinations with comparators defined. Comparisons with lists are made more natural by the variadic template method OneOf, which has a syntax like "if (str == OneOf("foo", "bar", "baz", str2))". OneOf remains valid for any combination of types for which the variables passed to OneOf can be constructed as the type of the variable they're comparing to.

RC Lib also provides an update of the C-style main function into a C++ one, with the RC_MAIN macro, used like "RC_MAIN { /* body of main here */; return 0 }". In it, the traditional "int argc, char *argv[]" variables are replaced by an "RC::Data1D<RC::RStr> args" which is bounds checked and coupled to all of the string parsing features of RStr. Additionally RC_MAIN registers a segfault handler which converts segfaults into a stack tracing RC::ErrorMsgFatal exception.

Miscellaneous convenience

Efficient Mersenne Twister random number generation is provided by RC::RND, which includes Get* functions for converting the randomness to the most common types of random data. True random number generation is provided more slowly by RC::EntropyRND which generates randomness on demand by monitoring the jitter of the high precision clock.

Cross-platform high precision timing and sleeping is provided by RC::Time. This class also provides a simplified and consistent method for working with the system date and time, as well as formatting and parsing date-time strings.

Debugging can be done more efficiently if rather than stepping through code inside of a debugger, one inserts the RC_DEBOUT() macro at critical locations. This macro takes no parameters or a list of variables, and outputs to stderr the filename, the line number, the name of each variable, and what its value is. It then flushes stderr to ensure output is complete before any subsequent program termination. This allows a low effort approach to rapidly tracing the flow of the program state, rather than slowly stepping through the program at a halted state.

Metaprogramming

Metaprogramming can provide significant increases in programmer efficiency and code maintainability by reducing code repeats. (But it must be used judiciously.)

An assortment of metaprogramming macros are provided in Macros.h, all defined with the macro-namespace convention of prefixing with RC_. For example, RC_ARGS_EACH(Macro,a,b,c,...) –> Macro(a)Macro(b)Macro(c)... provides the ability to wrap a list of parameters in a macro you define, which can be used to generate repeated sections of code on lists of types or objects. Similar capability is provided by RC_ARGS_LIST but for modifing a list of function parameters in place. RC_ARGS_PAIR provides the same capability but for type/value pairs. A number of other macro primitives are provided to facilitate writing your own metaprogramming macros.

A safer alternative to std::function (with stack-tracing exceptions) is provided with RC::Caller, which provides a wrapper for functors, static methods, and member methods. The member method constructor can receive an assigned object so that a single well-defined type Caller can be passed for a call to either a static method or a member method on a pre-assigned object. This facilitates passing callback handlers to a specific object without the signalling routine needing knowledge of the receiving object's type. For type-agnostic function handling, any templated Caller can be wrapped by an untemplated DynCaller, allowing arrays of methods of different types, and safe runtime extraction.

Project dependency

One of the most common reasons given to avoid using any particular library in a project is the concern over introducing a new dependency to the source code and/or the executable. This is often a legitimate concern, as external changes to a library can often break a piece of code developed in-house, and the use of any shared library requires it to be kept synchronized with the version used in development. RC Lib intentionally avoids this problem by being a fully header-only library. When you use RC Lib for a short prototype program, perhaps you use a shared copy of the include files. But when you use RC Lib in a project, you do not introduce a project dependency. You simply copy the RC directory into your source tree as-is, and it becomes an integral and stable part of your program. The permissive license provided was chosen to make this easy to do.

License

Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

Continue to the Examples page.

email address
— (c) 2015