Skip to content

sabbakumov/rst

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status

RST C++ library

This is the RST C++17 library. It is licensed under the Simplified BSD License.

Table of Contents

Getting the Code

git clone https://github.com/sabbakumov/rst.git
cd rst
mkdir build && cd build
cmake ..
cmake --build . && ./rst_tests

The library doesn't use exceptions and RTTI, and they are disabled by default. But you can enable compile support for them:

cmake .. -DRST_ENABLE_CXX_EXCEPTIONS=ON -DRST_ENABLE_CXX_RTTI=ON

You can enable ASAN build:

cmake .. -DRST_ENABLE_ASAN=ON

You can enable TSAN build:

cmake .. -DRST_ENABLE_TSAN=ON

You can enable UBSAN build:

cmake .. -DRST_ENABLE_UBSAN=ON

Codemap

Bind

Bind

Like std::bind() but the returned function object doesn't invoke rst::Bind's first callable object argument when rst::Bind's second argument (rst::WeakPtr or std::weak_ptr) was invalidated.

#include "rst/bind/bind.h"

class Controller : public rst::SupportsWeakPtr<Controller>,
                   public std::enable_shared_from_this<Controller> {
 public:
  void SpawnWorker() {
    Worker::StartNew(rst::Bind(&Controller::WorkComplete, AsWeakPtr()));
    Worker::StartNew(rst::Bind(&Controller::WorkComplete, weak_from_this()));
  }
  void WorkComplete(const Result& result) {}
};

class Worker {
 public:
  static void StartNew(std::function<void(const Result&)>&& callback) {
    new Worker(std::move(callback));
    // Asynchronous processing...
  }

 private:
  Worker(std::function<void(const Result&)>&& callback)
      : callback_(std::move(callback)) {}

  void DidCompleteAsynchronousProcessing(const Result& result) {
    callback_(result);  // Does nothing if the controller has been deleted.
    delete this;
  }

  const std::function<void(const Result&)> callback_;
};

DoNothing

Creates a placeholder function object that will implicitly convert into any std::function type, and does nothing when called.

#include "rst/bind/bind_helpers.h"

using MyCallback = std::function<void(bool arg)>;
void MyFunction(const MyCallback& callback) {
  callback(true);  // Uh oh...
}

MyFunction(MyCallback());  // ... this will crash!

// Instead, use rst::DoNothing():
MyFunction(rst::DoNothing());  // Can be run, will no-op.

NullFunction

Creates a null function object that will implicitly convert into any std::function type.

#include "rst/bind/bind_helpers.h"

using MyCallback = std::function<void(bool arg)>;
void MyFunction(const MyCallback& callback) {
  if (callback != nullptr)
    callback(true);
}

MyFunction(rst::NullFunction());

Check

Chromium-like checking macros for better programming error handling.

The RST_CHECK() macro will cause an immediate crash if its condition is not met. RST_DCHECK() is like RST_CHECK() but is only compiled in debug build. RST_NOTREACHED() is equivalent to RST_DCHECK(false). Here are some rules for using these:

Use RST_DCHECK() or RST_NOTREACHED() as assertions, e.g. to document pre- and post-conditions. A RST_DCHECK() means "this condition must always be true", not "this condition is normally true, but perhaps not in exceptional cases." Things like disk corruption or strange network errors are examples of exceptional circumstances that nevertheless should not result in RST_DCHECK() failure. A consequence of this is that you should not handle RST_DCHECK() failures, even if failure would result in a crash. Attempting to handle a RST_DCHECK() failure is a statement that the RST_DCHECK() can fail, which contradicts the point of writing the RST_DCHECK(). In particular, do not write code like the following:

#include "rst/check/check.h"

RST_DCHECK(foo);
if (!foo)  // Eliminate this code.
  ...

if (!bar) {  // Replace this whole conditional with "RST_DCHECK(bar);".
  RST_NOTREACHED();
  return;
}

Use RST_CHECK() if the consequence of a failed assertion would be a security vulnerability or a contract violation, where crashing is preferable.

If you want to do more complex logic in a debug build write the following:

#include "rst/check/check.h"
#include "rst/macros/macros.h"

#if RST_BUILDFLAG(DCHECK_IS_ON)
// Some complex logic in a debug build.
#endif  // RST_BUILDFLAG(DCHECK_IS_ON)

Clone

Used to explicitly copy the value and make it visible to the reader:

#include "rst/clone/clone.h"

void ConsumeString(std::string&& val);

const std::string s = ...;
ConsumeString(rst::Clone(s));

Defer

Executes a function object on scope exit.

#include "rst/defer/defer.h"

void Foo() {
  std::FILE* f = std::fopen(...);
  RST_DEFER([f]() { std::fclose(f); });
}

Files

WriteFile

Writes data to a file. Returns rst::FileError on error.

#include "rst/files/file_utils.h"

const std::string_view data = ...;
rst::Status status = rst::WriteFile("filename.txt", data);

WriteImportantFile

Like rst::WriteFile() but ensures that the file won't be corrupted by application crash during write. This is done by writing data to a file near the destination file, and then renaming the temporary file to the destination one.

#include "rst/files/file_utils.h"

const std::string_view data = ...;
rst::Status status = rst::WriteImportantFile("filename.txt", data);

ReadFile

Reads content from a file to a string. Returns rst::FileOpenError if the file can not be opened, rst::FileError on other error.

#include "rst/files/file_utils.h"

rst::StatusOr<std::string> content = rst::ReadFile("filename.txt");

GUID

#include "rst/guid/guid.h"

// Generates a 128-bit random GUID in the form of version 4 as described in RFC
// 4122, section 4.4. The format of GUID version 4 must be
// xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, where y is one of [8, 9, A, B]. The
// hexadecimal values "a" through "f" are output as lower case characters.
const rst::Guid guid;
const std::string guid_string = guid.AsString();

void TakeGuid(std::string_view guid);
TakeGuid(guid.AsStringView().value());

// Returns true if the input string conforms to the version 4 GUID format.
// Note that this does not check if the hexadecimal values "a" through "f" are
// in lower case characters, as Version 4 RFC says they're case insensitive.
// (Use IsValidGuidOutputString() for checking if the given string is valid
// output string).
RST_DCHECK(rst::Guid::IsValid(guid.AsStringView().value()));

// Returns true if the input string is valid version 4 GUID output string.
// This also checks if the hexadecimal values "a" through "f" are in lower case
// characters.
RST_DCHECK(rst::Guid::IsValidOutputString(guid.AsStringView().value()));

Logger

General logger component. Note that fatal logs abort the program.

#include "rst/logger/file_name_sink.h"
#include "rst/logger/file_ptr_sink.h"
#include "rst/logger/logger.h"

// Constructs a logger with a custom sink.
auto sink = rst::FileNameSink::Create("log.txt");
RST_CHECK(!sink.err());
rst::Logger logger(std::move(*sink));

auto sink = std::make_unique<rst::FilePtrSink>(stderr);
rst::Logger logger(std::move(sink));

// To get logger macros working.
rst::Logger::SetGlobalLogger(&logger);

RST_LOG_INFO("Init subsystem A");
// DLOG versions log only in a debug build.
RST_DLOG_WARNING("Init subsystem A.B");

RST_LOG_DEBUG("message");
RST_LOG_INFO("message");
RST_LOG_WARNING("message");
RST_LOG_ERROR("message");
RST_LOG_FATAL("message");

RST_DLOG_DEBUG("message");
RST_DLOG_INFO("message");
RST_DLOG_WARNING("message");
RST_DLOG_ERROR("message");
RST_DLOG_FATAL("message");

Macros

Macros

RST_DISALLOW_COPY

Put this in the declarations for a class to be uncopyable.

#include "rst/macros/macros.h"

class NonCopyable {
 private:
  RST_DISALLOW_COPY(NonCopyable);
};

RST_DISALLOW_ASSIGN

Put this in the declarations for a class to be unassignable.

#include "rst/macros/macros.h"

class NonAssignable {
 private:
  RST_DISALLOW_ASSIGN(NonAssignable);
};

RST_DISALLOW_COPY_AND_ASSIGN

Put this in the declarations for a class to be uncopyable and unassignable.

#include "rst/macros/macros.h"

class NonCopyAssignable {
 private:
  RST_DISALLOW_COPY_AND_ASSIGN(NonCopyAssignable);
};

RST_DISALLOW_IMPLICIT_CONSTRUCTORS

A macro to disallow all the implicit constructors, namely the default constructor, copy constructor and operator=() functions. This is especially useful for classes containing only static methods.

#include "rst/macros/macros.h"

class NonConstructible {
 private:
  RST_DISALLOW_IMPLICIT_CONSTRUCTORS(NonConstructible);
};

RST_CAT

This does a concatenation of two preprocessor arguments.

#include "rst/macros/macros.h"

static constexpr auto kAb = "cd";
const std::string s = RST_CAT(kA, b);
RST_DCHECK(s == kAb);

RST_BUILDFLAG

RST_BUILDFLAG() macro unmangles the names of the build flags in a way that looks natural, and gives errors if the flag is not defined. Normally in the preprocessor it's easy to make mistakes that interpret "you haven't done the setup to know what the flag is" as "flag is off".

#include "rst/macros/macros.h"

#define RST_BUILDFLAG_ENABLE_FOO() (true)

#if RST_BUILDFLAG(ENABLE_FOO)
// ...
#endif  // RST_BUILDFLAG(ENABLE_FOO)

OS

Macros to test the current OS.

#include "rst/macros/macros.h"
#include "rst/macros/os.h"

#if RST_BUILDFLAG(OS_WIN)
// Windows code.
#endif  // RST_BUILDFLAG(OS_WIN)

#if RST_BUILDFLAG(OS_ANDROID)
// Android code.
#endif  // RST_BUILDFLAG(OS_ANDROID)

Optimization

Enables the compiler to prioritize compilation using static analysis for likely paths within a boolean or switch branches.

#include "rst/macros/optimization.h"

if (RST_LIKELY(expression))
  return result;  // Faster if more likely.
else
  return 0;

if (RST_UNLIKELY(expression))
  return result;
else
  return 0;  // Faster if more likely.

switch (RST_LIKELY_EQ(x, 5)) {
  default:
  case 0:
  case 3:
  case 5:  // Faster if more likely.
}

Compilers can use the information that a certain branch is not likely to be taken (for instance, a RST_CHECK() failure) to optimize for the common case in the absence of better information.

Recommendation: modern CPUs dynamically predict branch execution paths, typically with accuracy greater than 97%. As a result, annotating every branch in a codebase is likely counterproductive; however, annotating specific branches that are both hot and consistently mispredicted is likely to yield performance improvements.

Thread Annotations

RST_GUARDED_BY

Documents if a shared field or global variable needs to be protected by a mutex. Allows the user to specify a particular mutex that should be held when accessing the annotated variable.

#include "rst/macros/thread_annotations.h"

std::mutex mtx;
int i RST_GUARDED_BY(mtx);

RST_PT_GUARDED_BY

Documents if the memory location pointed to by a pointer should be guarded by a mutex when dereferencing the pointer.

#include "rst/macros/thread_annotations.h"

std::mutex mtx;
int* p RST_PT_GUARDED_BY(mtx);
std::unique_ptr<int> p2 RST_PT_GUARDED_BY(mtx);

Note that a pointer variable to a shared memory location could itself be a shared variable.

#include "rst/macros/thread_annotations.h"

// |q|, guarded by |mtx1|, points to a shared memory location that is
// guarded by |mtx2|:
int* q RST_GUARDED_BY(mtx1) RST_PT_GUARDED_BY(mtx2);

Memory

WrapUnique

Chromium-like WrapUnique().

A helper to transfer ownership of a raw pointer to a std::unique_ptr<T>. It is usually used inside factory methods.

#include "rst/memory/memory.h"

class Foo {
 public:
  rst::NotNull<std::unique_ptr<Foo>> Create() {
    return rst::WrapUnique(new Foo());
  }

  rst::NotNull<std::unique_ptr<Foo>> CreateFromNotNull() {
    return rst::WrapUnique(rst::NotNull(new Foo()));
  }

 private:
  Foo() = default;
};

WeakPtr

Chromium-based WeakPtr.

Weak pointers are pointers to an object that do not affect its lifetime, and which may be invalidated (i.e. reset to nullptr) by the object, or its owner, at any time, most commonly when the object is about to be deleted.

Weak pointers are useful when an object needs to be accessed safely by one or more objects other than its owner, and those callers can cope with the object vanishing and e.g. tasks posted to it being silently dropped. Reference-counting such an object would complicate the ownership graph and make it harder to reason about the object's lifetime.

#include "rst/memory/weak_ptr.h"

class Controller : public rst::SupportsWeakPtr<Controller> {
 public:
  void SpawnWorker() { Worker::StartNew(AsWeakPtr()); }
  void WorkComplete(const Result& result) {}
};

class Worker {
 public:
  static void StartNew(rst::WeakPtr<Controller>&& controller) {
    new Worker(std::move(controller));
    // Asynchronous processing...
  }

 private:
  Worker(rst::WeakPtr<Controller>&& controller)
      : controller_(std::move(controller)) {}

  void DidCompleteAsynchronousProcessing(const Result& result) {
    rst::Nullable<Controller*> controller = controller_.GetNullable();
    if (controller != nullptr)
      controller->WorkComplete(result);
  }

  const rst::WeakPtr<Controller> controller_;
};

With this implementation a caller may use SpawnWorker() to dispatch multiple Workers and subsequently delete the Controller, without waiting for all Workers to have completed.

NoDestructor

Chromium-like NoDestructor class.

A wrapper that makes it easy to create an object of type T with static storage duration that:

  • is only constructed on first access
  • never invokes the destructor

Runtime constant example:

#include "rst/no_destructor/no_destructor.h"

const std::string& GetLineSeparator() {
  static const rst::NoDestructor<std::string> s(5, '-');
  return *s;
}

More complex initialization with a lambda:

#include "rst/no_destructor/no_destructor.h"

const std::string& GetSession() {
  static const rst::NoDestructor<std::string> session([] {
    std::string s(16);
    // ...
    return s;
  }());
  return *session;
}

NoDestructor<T> stores the object inline, so it also avoids a pointer indirection and memory allocation.

Note that since the destructor is never run, this will leak memory if used as a stack or member variable. Furthermore, a NoDestructor<T> should never have global scope as that may require a static initializer.

NotNull

NotNull

NotNull is a Microsoft GSL-like class that restricts a pointer or a smart pointer to only hold non-null values. It doesn't support constructing and assignment from nullptr_t. Also it asserts that the passed pointer is not nullptr.

#include "rst/not_null/not_null.h"

void Foo(rst::NotNul<int*>) {}

int i = 0;
Foo(&i);  // OK.
Foo(nullptr);  // Compilation error.
int* ptr = nullptr;
Foo(ptr);  // Asserts.

There are specializations for std::unique_ptr and std::shared_ptr. In order to take the inner smart pointer use Take() method:

rst::NotNull<std::unique_ptr<T>> p = ...;
std::unique_ptr<T> inner = std::move(p).Take();

Note std::move(p) is used to call Take(). It is a sign that p is in valid but unspecified state. No method other than destructor can be called.

Nullable

Nullable is a class that explicitly states that a pointer or a smart pointer can hold non-null values. It asserts that the object is checked for nullptr after construction.

#include "rst/not_null/not_null.h"

void Foo(rst::Nullable<int*> ptr) {
  if (ptr != nullptr)
    *ptr = 0;  // OK.
}

void Bar(rst::Nullable<int*> ptr) {
  *ptr = 0;  // Assert.
}

There are specializations for std::unique_ptr and std::shared_ptr. In order to take the inner smart pointer use Take() method:

rst::Nullable<std::unique_ptr<T>> p = ...;
std::unique_ptr<T> inner = std::move(p).Take();

Note std::move(p) is used to call Take(). It is a sign that p is in valid but unspecified state. No method other than destructor can be called.

Preferences

A set of preferences stored in a rst:PreferencesStore.

rst::Preferences need to be registered with a type, dotted path, and a default value before they can be used. A type can be any of the rst::Value types.

#include "rst/preferences/memory_preferences_store.h"
#include "rst/preferences/preferences.h"

rst::Preferences preferences(std::make_unique<rst::MemoryPreferencesStore>());

preferences.RegisterIntPreference("int.preference", 10);
RST_DCHECK(preferences.GetInt("int.preference") == 10);

preferences.SetInt("int.preference", 20);
RST_DCHECK(preferences.GetInt("int.preference") == 20);

// etc.

RandomDevice

Returns a reference to a singleton in order to have only one random device per thread.

#include "rst/random/random_device.h"

std::random_device& GetRandomDevice();

RTTI

LLVM-based RTTI.

#include "rst/rtti/rtti.h"

class FileError : public rst::ErrorInfo<FileError> {
 public:
  explicit FileError(std::string&& message);
  ~FileError();

  const std::string& AsString() const override;

  // Important to have this non-const field!
  static char id_;

 private:
  const std::string message_;
};

rst::Status status = Bar();
if (status.err() &&
    rst::dyn_cast<FileError>(status.GetError()) != nullptr) {
  // File doesn't exist.
}

STL

Algorithm

Container-based versions of algorithmic functions within the C++ standard library.

#include "rst/stl/algorithm.h"

template <class C>
void c_sort(C& c);

template <class C, class Compare>
void c_sort(C& c, Compare&& comp);

template <class C>
void c_stable_sort(C& c);

template <class C, class Compare>
void c_stable_sort(C& c, Compare&& comp);

template <class C, class UnaryPredicate>
auto c_find_if(C& c, UnaryPredicate&& pred);

template <class C, class Compare>
void c_push_heap(C& c, Compare&& comp);

template <class C, class Compare>
void c_pop_heap(C& c, Compare&& comp);

template <class C, class Compare>
bool c_is_sorted(const C& c, Compare&& comp);

template <class C, class URBG>
void c_shuffle(C& c, URBG&& g);

HashCombine

Boost-like function to create a hash value from several variables. It can take any type that std::hash does.

#include "rst/stl/hash.h"

struct Point {
  Point(const int x, const int y) : x(x), y(y) {}

  int x = 0;
  int y = 0;
};

bool operator==(const Point lhs, const Point rhs) {
  return (lhs.x == rhs.x) && (lhs.y == rhs.y);
}

namespace std {

template <>
struct hash<Point> {
  size_t operator()(const Point point) const {
    return rst::HashCombine({point.x, point.y});
  }
};

}  // namespace std

Reversed

Returns a Chromium-like container adapter usable in a range-based for statement for iterating a reversible container in reverse order.

#include "rst/stl/reversed.h"

std::vector<int> v = ...;
for (auto i : rst::Reversed(v)) {
  // Iterates through v from back to front.
}

StringResizeUninitialized

Like std::string::resize(), except any new characters added to a string as a result of resizing may be left uninitialized, rather than being filled with '\0' bytes. Typically used when code is then going to overwrite the backing store of the std::string with known data.

#include "rst/stl/resize_uninitialized.h"

std::string s;
rst::StringResizeUninitialized(&s, 10);
void* ptr = ...
std::memcpy(s.data(), ptr, 10);

TakeFunction

Like std::move() for std::function except that it also assigns nullptr to a moved argument.

#include "rst/stl/function.h"

std::function<void()> f = ...;
auto moved_f = std::move(f);  // f is in a valid but unspecified state
                              // after the call.
std::function<void()> f = ...;
auto moved_f = rst::TakeFunction(std::move(f));  // f is nullptr.

VectorBuilder

Allows in-place initialization of a std::vector of move-only objects.

#include "rst/stl/vector_builder.h"

const std::vector<std::unique_ptr<int>> vec =
    rst::VectorBuilder<std::unique_ptr<int>>()
        .emplace_back(std::make_unique<int>(1))
        .emplace_back(std::make_unique<int>(-1))
        .Build();

Status

Status Macros

RST_TRY

Macro to allow exception-like handling of rst::Status return values.

If the evaluation of statement results in an error, returns that error from the current function.

#include "rst/status/status_macros.h"

rst::Status Foo();

rst::Status Bar() {  // Or can be rst::StatusOr<T> Bar().
  RST_TRY(Foo());
  ...
}

RST_TRY_ASSIGN

Macro to allow exception-like handling of rst::StatusOr return values.

Assigns the result of evaluation of statement to lvalue and if it results in an error, returns that error from the current function.

lvalue should be an existing non-const variable accessible in the current scope.

RST_TRY_ASSIGN() expands into multiple statements; it cannot be used in a single statement (e.g. as the body of an if statement without {})!

#include "rst/status/status_macros.h"

rst::StatusOr<MyType> Foo();

rst::Status Bar() {  // Or can be rst::StatusOr<T> Bar().
  rst::StatusOr<MyType> existing_var = ...;
  RST_TRY_ASSIGN(existing_var, Foo());
  ...
}

RST_TRY_ASSIGN_UNWRAP

Macro to allow exception-like handling of rst::StatusOr return values.

Assigns the unwrapped result of evaluation of statement to lvalue and if it results in an error, returns that error from the current function.

lvalue should be an existing non-const variable accessible in the current scope.

#include "rst/status/status_macros.h"

rst::StatusOr<MyType> Foo();

rst::Status Bar() {  // Or can be rst::StatusOr<T> Bar().
  MyType existing_var = ...;
  RST_TRY_ASSIGN_UNWRAP(existing_var, Foo());
  ...
}

RST_TRY_CREATE

Macro to allow exception-like handling of rst::StatusOr return values.

Creates lvalue and assigns the result of evaluation of statement to it and if it results in an error, returns that error from the current function.

lvalue should be a new variable.

RST_TRY_CREATE() expands into multiple statements; it cannot be used in a single statement (e.g. as the body of an if statement without {})!

#include "rst/status/status_macros.h"

rst::StatusOr<MyType> Foo();

rst::Status Bar() {  // Or can be rst::StatusOr<T> Bar().
  RST_TRY_CREATE(rst::StatusOr<MyType>, var1, Foo());
  RST_TRY_CREATE(auto, var2, Foo());
  ...
}

StatusOr

A Google-like StatusOr class for recoverable error handling. It's impossible to ignore an error, since error checking is mandatory. rst::StatusOr destructor examines if the checked flag is set. All instances must be checked before destruction, even if they're moved-assigned or constructed from success values that have already been checked. This enforces checking through all levels of the call stack.

#include "rst/status/status_or.h"

rst::Status Bar() {  // Or can be rst::StatusOr<T> Bar().
  rst::StatusOr<std::string> foo = Foo();
  if (foo.err())
    return std::move(foo).TakeStatus();
  ...
}

// Or:
rst::Status Bar() {  // Or can be rst::StatusOr<T> Bar().
  RST_TRY_CREATE(auto, foo, Foo());
  RST_TRY_CREATE(rst::StatusOr<std::string>, foo, Foo());
  RST_TRY_ASSIGN(foo, Foo());
  std::cout << *foo << ", " << foo->size() << std::endl;
  ...
}

// Check specific error:
rst::StatusOr<std::string> status = Foo();
if (status.err() &&
    rst::dyn_cast<FileOpenError>(status.GetError()) != nullptr) {
  // File doesn't exist.
}

rst::Status Bad() {  // Or can be rst::StatusOr<T> Bar().
  Foo();  // Doesn't compile, since the class is marked as [[nodiscard]].
  {
    rst::StatusOr<std::string> foo = Foo();
    // DCHECK's, since error handling is ignored.
  }
  ...
}

Status

A Google-like Status class for recoverable error handling. It's impossible to ignore an error, since error checking is mandatory. rst::Status destructor examines if the checked flag is set. All instances must be checked before destruction, even if they're moved-assigned or constructed from success values that have already been checked. This enforces checking through all levels of the call stack.

#include "rst/status/status.h"

rst::Status Bar() {  // Or can be rst::StatusOr<T> Bar().
  rst::Status status = Foo();
  if (status.err())
    return status;
  ...
}

// Or:
rst::Status Bar() {  // Or can be rst::StatusOr<T> Bar().
  RST_TRY(Foo());
  ...
}

// Check specific error:
rst::Status status = Foo();
if (status.err() &&
    rst::dyn_cast<FileOpenError>(status.GetError()) != nullptr) {
  // File doesn't exist.
}

rst::Status Bad() {  // Or can be rst::StatusOr<T> Bar().
  Foo();  // Doesn't compile, since the class is marked as [[nodiscard]].
  {
    rst::Status status = Foo();
    // DCHECK's, since error handling is ignored.
  }
  ...
}

Strings

Format

This function is for efficiently performing string formatting.

Unlike printf-style format specifiers, rst::Format() functions do not need to specify the type of the arguments. Supported arguments following the format string, such as strings, string_views, ints, floats, and bools, are automatically converted to strings during the formatting process. See below for a full list of supported types.

rst::Format() does not allow you to specify how to format a value, beyond the default conversion to string. For example, you cannot format an integer in hex.

The format string uses identifiers indicated by a {} like in Python.

A '{{' or '}}' sequence in the format string causes a literal '{' or '}' to be output.

#include "rst/strings/format.h"

std::string s = rst::Format("{} purchased {} {}", {"Bob", 5, "Apples"});
RST_DCHECK(s == "Bob purchased 5 Apples");

Supported types:

  • std::string_view, std::string, const char*
  • short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long
  • float, double, long double (printed as if %g is specified for printf())
  • bool (printed as "true" or "false")
  • char
  • enums (printed as underlying integer type)

If an invalid format string is provided, rst::Format() asserts in a debug build.

StrCat

This function is for efficiently performing merging an arbitrary number of strings or numbers into one string, and is designed to be the fastest possible way to construct a string out of a mix of raw C strings, string_view elements, std::string value, boolean and numeric values. rst::StrCat() is generally more efficient on string concatenations involving more than one unary operator, such as a + b + c or a += b + c, since it avoids the creation of temporary string objects during string construction.

Supported arguments such as strings, string_views, ints, floats, and bools, are automatically converted to strings during the concatenation process. See below for a full list of supported types.

#include "rst/strings/str_cat.h"

std::string s = rst::StrCat({"Bob", " purchased ", 5, " ", "Apples"});
RST_DCHECK(s == "Bob purchased 5 Apples");

Supported types:

  • std::string_view, std::string, const char*
  • short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long
  • float, double, long double (printed as if %g is specified for printf())
  • bool (printed as "true" or "false")
  • char
  • enums (printed as underlying integer type)

TaskRunner

PollingTaskRunner

Task runner that is supposed to run tasks on the same thread.

#include "rst/task_runner/polling_task_runner.h"

std::function<std::chrono::nanoseconds()> time_function = ...;
rst::PollingTaskRunner task_runner(std::move(time_function));
for (;; task_runner.RunPendingTasks()) {
  // ...
  std::function<void()> task = ...;
  task_runner.PostTask(std::move(task));
  task = ...;
  task_runner.PostDelayedTask(std::move(task), std::chrono::seconds(1));
  // ...
}

ThreadPoolTaskRunner

Task runner that is supposed to run tasks on dedicated threads that have their keep alive time. After that time of inactivity the threads stop.

#include "rst/task_runner/thread_pool_task_runner.h"

std::function<std::chrono::nanoseconds()> time_function = ...;
size_t max_threads_num = ...;
std::chrono::nanoseconds keep_alive_time = ...;
rst::ThreadPoolTaskRunner task_runner(max_threads_num,
                                      std::move(time_function),
                                      keep_alive_time);
...
std::function<void()> task = ...;
task_runner.PostTask(std::move(task));
task = ...;
task_runner.PostDelayedTask(std::move(task), std::chrono::seconds(1));
...

// Posts a single |task| and waits for all |iterations| to complete before
// returning. The current index of iteration is passed to each invocation.
std::function<void(size_t)> task = ...;
constexpr size_t iterations = 100;
task_runner.ApplyTaskSync(std::move(task), iterations);

Threading

Barrier

Provides a thread-coordination mechanism that allows a set of participating threads to block until an operation is completed. The value of the counter is initialized on creation. Threads block until the counter is decremented to zero.

#include "rst/threading/barrier.h"

rst::Barrier barrier(5);

std::vector<std::thread> threads;
for (auto i = 0; i < 5; i++)
  threads.emplace_back([&barrier]() { barrier.CountDown(); });

barrier.Wait();
// Synchronization point.

Timer

OneShotTimer

rst::OneShotTimer provides a simple timer API. As the name suggests, rst::OneShotTimer calls back once after a time delay expires.

rst::OneShotTimer cancels the timer when it goes out of scope, which makes it easy to ensure that you do not get called when your object has gone out of scope. Just instantiate a timer as a member variable of the class for which you wish to receive timer events.

#include "rst/timer/one_shot_timer.h"

class MyClass {
 public:
  void DelayDoingStuff() {
    timer_.Start(std::bind(&MyClass::DoStuff, this),
                 std::chrono::seconds(1));
  }

 private:
  void DoStuff() {
    // This method is called after 1 second.
  }

  rst::OneShotTimer timer_{&GetTaskRunner};
};

Type

A Chromium-like StrongAlias type.

A type-safe alternative for a typedef or a using directive like in Golang.

The motivation is to disallow several classes of errors:

using Orange = int;
using Apple = int;
Apple apple(2);
Orange orange = apple;  // Orange should not be able to become an Apple.
Orange x = orange + apple;  // Shouldn't add Oranges and Apples.
if (orange > apple);  // Shouldn't compare Apples to Oranges.
void foo(Orange);
void foo(Apple);  // Redefinition.

Type may instead be used as follows:

#include "rst/type/type.h"

using Orange = rst::Type<class OrangeTag, int>;
using Apple = rst::Type<class AppleTag, int>;
Apple apple(2);
Orange orange = apple;  // Does not compile.
Orange other_orange = orange;  // Compiles, types match.
Orange x = orange + apple;  // Does not compile.
Orange y = Orange(orange.value() + apple.value());  // Compiles.
if (orange > apple);  // Does not compile.
if (orange > other_orange);  // Compiles.
void foo(Orange);
void foo(Apple);  // Compiles into separate overload.

TagType is an empty tag class (also called "phantom type") that only serves the type system to differentiate between different instantiations of the template.

Value

A Chromium-like JSON Value class.

This is a recursive data storage class intended for storing settings and other persistable data.

A rst::Value represents something that can be stored in JSON or passed to/from JavaScript. As such, it is not a generalized variant type, since only the types supported by JavaScript/JSON are supported.

In particular this means that there is no support for int64_t or unsigned numbers. Writing JSON with such types would violate the spec. If you need something like this, either use a double or make a string value containing the number you want.

Construction:

rst::Value is directly constructible from bool, int32_t, int64_t (checking that the modulo of the value is <= 2^53 - 1, double (excluding NaN and inf), const char*, std::string, rst::Value::Array, and rst::Value::Object.

Copying:

rst::Value does not support C++ copy semantics to make it harder to accidentally copy large values. Instead, use Clone() to manually create a deep copy.

Reading:

GetBool(), GetInt(), etc. assert that the rst::Value has the correct type() before returning the contained value. bool, int, double are returned by value. std::string, rst::Value::Object, rst::Value::Array are returned by reference.

Assignment:

It is not possible to directly assign bool, int, etc. to a rst::Value. Instead, wrap the underlying type in rst::Value before assigning.

Objects support:

FindKey(): Finds a value by std::string_view key, returning nullptr if the key is not present. FindKeyOfType(): Finds a value by std::string_view key, returning nullptr if the key is not present or the type() of the value is not what is needed. FindBoolKey(), FindIntKey(), ...: Similar to FindKey(), but ensures that the rst::Value also has the correct type(). bool, int, double are returned by std::optional<>. std::string, rst::Value::Object, rst::Value::Array are returned by rst::Nullable pointer. SetKey(): Associates a rst::Value with a std::string key. RemoveKey(): Removes the key from this object, if present.

Objects also support an additional set of helper methods that operate on "paths": FindPath(), and SetPath(). Dotted paths are a convenience method of naming intermediate nested objects, separating the components of the path using '.' characters. For example, finding a path on a rst::Value::Object using the dotted path:

"aaa.bbb.ccc"

will first look for a rst::Value::Object associated with the key "aaa", then another rst::Value::Object under the "aaa" object associated with the key "bbb", and then a rst::Value under the "bbb" object associated with the key "ccc".

If a path only has one component (i.e. has no dots), please use the regular, non-path APIs.