Skip to content

Repository files navigation

cpp_toolbelt

A collection of useful C++ classes and utility functions for systems programming.

Table of Contents

Installation

Using Bazel

Add this to your MODULE.bazel or WORKSPACE file:

http_archive(
    name = "toolbelt",
    urls = ["https://github.com/dallison/cpp_toolbelt/archive/refs/tags/A.B.C.tar.gz"],
    strip_prefix = "cpp_toolbelt-A.B.C",
)

Replace A.B.C with the version you want (e.g., 1.0.3).

Add a dependency to your BUILD.bazel targets:

deps = [
    # ...
    "@toolbelt//toolbelt",
],

Using CMake

include(FetchContent)
FetchContent_Declare(
    toolbelt
    GIT_REPOSITORY https://github.com/dallison/cpp_toolbelt.git
    GIT_TAG <version-tag>
)
FetchContent_MakeAvailable(toolbelt)

target_link_libraries(your_target PRIVATE toolbelt)

Classes

FileDescriptor

A reference-counted wrapper around UNIX file descriptors. Automatically closes the file descriptor when all references are destroyed.

Header: toolbelt/fd.h

API

class FileDescriptor {
public:
    FileDescriptor();
    explicit FileDescriptor(int fd, bool owned = true);
    FileDescriptor(const FileDescriptor &f);
    FileDescriptor(FileDescriptor &&f);
    
    void Close();
    bool IsOpen() const;
    bool IsATTY() const;
    int RefCount() const;
    struct pollfd GetPollFd();
    bool Valid() const;
    int Fd() const;
    void SetFd(int fd, bool owned = true);
    void Reset();
    void Release();
    void ForceClose();
    bool IsNonBlocking() const;
    absl::Status SetNonBlocking();
    absl::Status SetCloseOnExec();
    absl::StatusOr<ssize_t> Read(void* buffer, size_t length, 
                                  const co::Coroutine* c = nullptr);
    absl::StatusOr<ssize_t> Write(const void* buffer, size_t length,
                                  const co::Coroutine* c = nullptr);
};

Example

#include "toolbelt/fd.h"

// Create from an existing file descriptor
int fd = open("/path/to/file", O_RDONLY);
toolbelt::FileDescriptor file(fd);

// Copy creates a new reference (cheap, just increments ref count)
toolbelt::FileDescriptor file2 = file;
assert(file.RefCount() == 2);

// File descriptor is automatically closed when last reference is destroyed
file.Close();  // Still open, file2 still references it
// file2 goes out of scope -> file descriptor is closed

// Read from file descriptor
char buffer[1024];
auto result = file.Read(buffer, sizeof(buffer));
if (result.ok()) {
    size_t bytes_read = *result;
    // Process data...
}

Socket Classes

The socket classes provide a high-level interface to various socket types. They are coroutine-aware and work with the co coroutine library.

Header: toolbelt/sockets.h

InetAddress

Represents an IPv4 internet address and port.

class InetAddress {
public:
    InetAddress();
    InetAddress(int port);  // INADDR_ANY with given port
    InetAddress(const in_addr &ip, int port);
    InetAddress(const std::string &hostname, int port);
    InetAddress(const struct sockaddr_in &addr);
    
    const sockaddr_in &GetAddress() const;
    socklen_t GetLength() const;
    bool Valid() const;
    in_addr IpAddress() const;  // Host byte order
    int Port() const;           // Host byte order
    void SetPort(int port);
    std::string ToString() const;
    
    static InetAddress BroadcastAddress(int port);
    static InetAddress AnyAddress(int port);
};

Example

#include "toolbelt/sockets.h"

// Create address from hostname and port
toolbelt::InetAddress addr("localhost", 8080);

// Create address for any interface on port 8080
auto any_addr = toolbelt::InetAddress::AnyAddress(8080);

// Get address components
in_addr ip = addr.IpAddress();
int port = addr.Port();
std::string str = addr.ToString();  // "127.0.0.1:8080"

VirtualAddress

Represents a virtual socket address (VSOCK) used for communication between VMs and the host.

class VirtualAddress {
public:
    VirtualAddress();
    VirtualAddress(uint32_t port);
    VirtualAddress(uint32_t cid, uint32_t port);
    
    uint32_t Cid() const;
    uint32_t Port() const;
    void SetPort(uint32_t port);
    std::string ToString() const;
    
    static VirtualAddress HypervisorAddress(uint32_t port);
    static VirtualAddress HostAddress(uint32_t port);
    static VirtualAddress AnyAddress(uint32_t port);
    #if defined(__linux__)
    static VirtualAddress LocalAddress(uint32_t port);
    #endif
};

SocketAddress

A variant type that can hold an InetAddress, VirtualAddress, or Unix socket path (string).

class SocketAddress {
public:
    SocketAddress();
    SocketAddress(const InetAddress &addr);
    SocketAddress(const VirtualAddress &addr);
    SocketAddress(const std::string &addr);  // Unix socket path
    
    const InetAddress &GetInetAddress() const;
    const VirtualAddress &GetVirtualAddress() const;
    const std::string &GetUnixAddress() const;
    std::string ToString() const;
    bool Valid() const;
    int Type() const;  // kAddressInet, kAddressVirtual, or kAddressUnix
    int Port() const;
};

Socket

Base class for all socket types. Provides common functionality for sending and receiving data.

class Socket {
public:
    void Close();
    bool Connected() const;
    absl::StatusOr<ssize_t> Receive(char *buffer, size_t buflen,
                                    const co::Coroutine *c = nullptr);
    absl::StatusOr<ssize_t> Send(const char *buffer, size_t length,
                                 const co::Coroutine *c = nullptr);
    absl::StatusOr<ssize_t> ReceiveMessage(char *buffer, size_t buflen,
                                           const co::Coroutine *c = nullptr);
    absl::StatusOr<std::vector<char>> ReceiveVariableLengthMessage(
        const co::Coroutine *c = nullptr);
    absl::StatusOr<ssize_t> SendMessage(char *buffer, size_t length,
                                        const co::Coroutine *c = nullptr);
    absl::Status SetNonBlocking();
    FileDescriptor GetFileDescriptor() const;
    absl::Status SetCloseOnExec();
    bool IsNonBlocking() const;
    bool IsBlocking() const;
};

UnixSocket

A Unix Domain socket for inter-process communication.

class UnixSocket : public Socket {
public:
    UnixSocket();
    explicit UnixSocket(int fd, bool connected = false);
    
    absl::Status Bind(const std::string &pathname, bool listen);
    absl::Status Connect(const std::string &pathname);
    absl::StatusOr<UnixSocket> Accept(const co::Coroutine *c = nullptr) const;
    absl::Status SendFds(const std::vector<FileDescriptor> &fds,
                        const co::Coroutine *c = nullptr);
    absl::Status ReceiveFds(std::vector<FileDescriptor> &fds,
                            const co::Coroutine *c = nullptr);
    std::string BoundAddress() const;
    absl::StatusOr<std::string> GetPeerName() const;
    absl::StatusOr<std::string> LocalAddress() const;
};

Example

#include "toolbelt/sockets.h"

// Server side
toolbelt::UnixSocket server;
server.Bind("/tmp/mysocket", true);  // true = listen

// In a coroutine or event loop
auto client = server.Accept(coroutine);
if (client.ok()) {
    char buffer[1024];
    auto result = client->Receive(buffer, sizeof(buffer), coroutine);
    // Process received data...
}

// Client side
toolbelt::UnixSocket client;
client.Connect("/tmp/mysocket");
client.Send("Hello", 5, coroutine);

TCPSocket

A TCP socket for network communication.

class TCPSocket : public NetworkSocket {
public:
    TCPSocket();
    explicit TCPSocket(int fd, bool connected = false);
    
    absl::Status Bind(const InetAddress &addr, bool listen);
    absl::StatusOr<TCPSocket> Accept(const co::Coroutine *c = nullptr) const;
    absl::StatusOr<InetAddress> LocalAddress(int port) const;
    absl::StatusOr<InetAddress> GetPeerName() const;
};

Example

#include "toolbelt/sockets.h"

// Server
toolbelt::TCPSocket server;
toolbelt::InetAddress addr(8080);
server.Bind(addr, true);  // Listen on port 8080

// Accept connections
auto client = server.Accept(coroutine);
if (client.ok()) {
    char buffer[1024];
    auto result = client->Receive(buffer, sizeof(buffer), coroutine);
}

// Client
toolbelt::TCPSocket client;
toolbelt::InetAddress server_addr("example.com", 8080);
client.Connect(server_addr);
client.Send("Hello", 5, coroutine);

UDPSocket

A UDP socket for datagram communication.

class UDPSocket : public NetworkSocket {
public:
    UDPSocket();
    explicit UDPSocket(int fd, bool connected = false);
    
    absl::Status Bind(const InetAddress &addr);
    absl::Status JoinMulticastGroup(const InetAddress &addr);
    absl::Status LeaveMulticastGroup(const InetAddress &addr);
    absl::Status SendTo(const InetAddress &addr, const void *buffer,
                       size_t length, const co::Coroutine *c = nullptr);
    absl::StatusOr<ssize_t> Receive(void *buffer, size_t buflen,
                                    const co::Coroutine *c = nullptr);
    absl::StatusOr<ssize_t> ReceiveFrom(InetAddress &sender, void *buffer,
                                        size_t buflen,
                                        const co::Coroutine *c = nullptr);
    absl::Status SetBroadcast();
    absl::Status SetMulticastLoop();
};

Example

#include "toolbelt/sockets.h"

// Server
toolbelt::UDPSocket socket;
toolbelt::InetAddress addr(8080);
socket.Bind(addr);

toolbelt::InetAddress sender;
char buffer[1024];
auto result = socket.ReceiveFrom(sender, buffer, sizeof(buffer), coroutine);
if (result.ok()) {
    // Process datagram from sender
}

// Client
toolbelt::UDPSocket socket;
toolbelt::InetAddress server("example.com", 8080);
socket.SendTo(server, "Hello", 5, coroutine);

VirtualStreamSocket

A virtual stream socket for VM-to-host communication using VSOCK.

class VirtualStreamSocket : public Socket {
public:
    VirtualStreamSocket();
    explicit VirtualStreamSocket(int fd, bool connected = false);
    
    absl::Status Connect(const VirtualAddress &addr);
    absl::Status Bind(const VirtualAddress &addr, bool listen);
    absl::StatusOr<VirtualStreamSocket> Accept(
        const co::Coroutine *c = nullptr) const;
    absl::StatusOr<VirtualAddress> LocalAddress(uint32_t port) const;
    const VirtualAddress &BoundAddress() const;
    absl::StatusOr<VirtualAddress> GetPeerName() const;
    uint32_t Cid() const;
};

StreamSocket

A type-erased wrapper that can hold a TCPSocket, VirtualStreamSocket, or UnixSocket. Useful when you need to work with different socket types uniformly.

class StreamSocket {
public:
    StreamSocket();
    
    absl::Status Bind(const SocketAddress &addr, bool listen);
    absl::Status Connect(const SocketAddress &addr);
    absl::StatusOr<StreamSocket> Accept(const co::Coroutine *c = nullptr) const;
    
    TCPSocket &GetTCPSocket();
    VirtualStreamSocket &GetVirtualStreamSocket();
    UnixSocket &GetUnixSocket();
    
    SocketAddress BoundAddress() const;
    void Close();
    bool Connected() const;
    absl::StatusOr<ssize_t> Receive(char *buffer, size_t buflen,
                                    const co::Coroutine *c = nullptr);
    absl::StatusOr<ssize_t> Send(const char *buffer, size_t length,
                                 const co::Coroutine *c = nullptr);
    // ... other Socket methods
};

Example

#include "toolbelt/sockets.h"

// Works with any socket type
toolbelt::StreamSocket socket;

// Can bind to TCP, Unix, or VSOCK addresses
toolbelt::SocketAddress addr = toolbelt::InetAddress(8080);
// or: toolbelt::SocketAddress addr("/tmp/socket");
// or: toolbelt::SocketAddress addr(toolbelt::VirtualAddress(1234));

socket.Bind(addr, true);
auto client = socket.Accept(coroutine);

Logger

A level-aware logger that supports colored output, multiple output modes, and themes.

Header: toolbelt/logging.h

API

enum class LogLevel {
    kVerboseDebug,
    kDebug,
    kInfo,
    kWarning,
    kError,
    kFatal,
};

enum class LogDisplayMode {
    kPlain,
    kColor,
    kColumnar,
};

enum class LogTheme {
    kDefault,
    kLight,
    kDark,
};

class Logger {
public:
    Logger();
    Logger(const std::string &subsystem, bool enabled = true,
           LogTheme theme = LogTheme::kDefault,
           LogDisplayMode mode = LogDisplayMode::kPlain);
    Logger(LogLevel min);
    
    void Enable();
    void Disable();
    absl::Status SetTeeFile(const std::string &filename, bool truncate = true);
    void SetTeeStream(FILE *stream);
    void Log(LogLevel level, const char *fmt, ...);
    void VLog(LogLevel level, const char *fmt, va_list ap);
    void Log(LogLevel level, uint64_t timestamp,
             const std::string &source, std::string text);
    void SetTheme(LogTheme theme);
    void SetLogLevel(LogLevel l);
    void SetLogLevel(const std::string &s);  // "verbose", "debug", "info", etc.
    LogLevel GetLogLevel() const;
    void SetOutputStream(FILE *stream);
};

Example

#include "toolbelt/logging.h"

// Create logger with subsystem name
toolbelt::Logger logger("myapp", true, toolbelt::LogTheme::kDefault,
                       toolbelt::LogDisplayMode::kColor);

// Set minimum log level
logger.SetLogLevel(toolbelt::LogLevel::kInfo);
// or
logger.SetLogLevel("info");

// Log messages
logger.Log(toolbelt::LogLevel::kInfo, "Application started");
logger.Log(toolbelt::LogLevel::kDebug, "Debug value: %d", 42);
logger.Log(toolbelt::LogLevel::kWarning, "Warning: %s", "Something happened");
logger.Log(toolbelt::LogLevel::kError, "Error code: %d", errno);

// Tee output to a file
logger.SetTeeFile("/var/log/myapp.log");

// Disable logging temporarily
logger.Disable();
// ... do something ...
logger.Enable();

MutexLock and RWLock

RAII wrappers for pthread mutexes and read-write locks.

Header: toolbelt/mutex.h

API

class MutexLock {
public:
    MutexLock(pthread_mutex_t *mutex);
    ~MutexLock();
};

class RWLock {
public:
    RWLock(pthread_rwlock_t *lock, bool read);
    ~RWLock();
    
    void ReadLock();
    void WriteLock();
    void Unlock();
};

Example

#include "toolbelt/mutex.h"
#include <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

{
    toolbelt::MutexLock lock(&mutex);
    // Critical section - mutex is automatically unlocked when lock goes out of scope
    // Do work...
}

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

{
    toolbelt::RWLock lock(&rwlock, true);  // true = read lock
    // Multiple readers allowed
    // Read data...
}

{
    toolbelt::RWLock lock(&rwlock, false);  // false = write lock
    // Exclusive write access
    // Write data...
}

BitSet

A fixed-size bitset that allows allocation of individual bits.

Header: toolbelt/bitset.h

API

template <int Size>
class BitSet {
public:
    BitSet();
    void Init();
    absl::StatusOr<int> Allocate(const std::string &type);
    bool IsEmpty() const;
    void Set(int b);
    void Clear(int b);
    bool IsSet(int b) const;
};

Example

#include "toolbelt/bitset.h"

// Create a bitset with 256 bits
toolbelt::BitSet<256> bitset;

// Allocate the first available bit
auto result = bitset.Allocate("resource");
if (result.ok()) {
    int bit_index = *result;
    // Use the bit...
    
    // Check if bit is set
    if (bitset.IsSet(bit_index)) {
        // ...
    }
    
    // Clear the bit when done
    bitset.Clear(bit_index);
}

// Check if all bits are clear
if (bitset.IsEmpty()) {
    // All resources freed
}

Pipe

A pipe for inter-process or inter-coroutine communication. Supports both blocking and non-blocking I/O, and can work with coroutines.

Header: toolbelt/pipe.h

API

class Pipe {
public:
    static absl::StatusOr<Pipe> Create();
    static absl::StatusOr<Pipe> CreateWithFlags(int flags);
    static absl::StatusOr<Pipe> Create(int r, int w);
    
    Pipe();
    Pipe(int r, int w);
    
    absl::Status Open(int flags = 0);
    FileDescriptor &ReadFd();
    FileDescriptor &WriteFd();
    void SetReadFd(int fd);
    void SetWriteFd(int fd);
    void Close();
    void ForceClose();
    absl::Status SetNonBlocking(bool read, bool write);
    absl::StatusOr<size_t> GetPipeSize();
    absl::Status SetPipeSize(size_t size);
    absl::StatusOr<ssize_t> Read(char *buffer, size_t length,
                                 const co::Coroutine *c = nullptr);
    absl::StatusOr<ssize_t> Write(const char *buffer, size_t length,
                                  const co::Coroutine *c = nullptr);
};

template <typename T>
class SharedPtrPipe : public Pipe {
public:
    static absl::StatusOr<SharedPtrPipe<T>> Create();
    static absl::StatusOr<SharedPtrPipe<T>> Create(int r, int w);
    
    absl::StatusOr<std::shared_ptr<T>> Read(const co::Coroutine *c = nullptr);
    absl::Status Write(std::shared_ptr<T> p, const co::Coroutine *c = nullptr);
};

Example

#include "toolbelt/pipe.h"

// Create a pipe
auto pipe_result = toolbelt::Pipe::Create();
if (!pipe_result.ok()) {
    // Handle error
    return;
}
toolbelt::Pipe pipe = *pipe_result;

// Set non-blocking mode
pipe.SetNonBlocking(true, true);

// Write data (in a coroutine or thread)
char data[] = "Hello, World!";
auto write_result = pipe.Write(data, strlen(data), coroutine);

// Read data (in another coroutine or thread)
char buffer[1024];
auto read_result = pipe.Read(buffer, sizeof(buffer), coroutine);
if (read_result.ok()) {
    size_t bytes_read = *read_result;
    // Process data...
}

// SharedPtrPipe for sending shared_ptr objects (same process only)
auto shared_pipe = toolbelt::SharedPtrPipe<MyClass>::Create();
if (shared_pipe.ok()) {
    auto obj = std::make_shared<MyClass>();
    shared_pipe->Write(obj, coroutine);
    
    auto received = shared_pipe->Read(coroutine);
    if (received.ok()) {
        std::shared_ptr<MyClass> obj = *received;
        // Use object...
    }
}

Table

A table formatter for displaying tabular data with colors and sorting.

Header: toolbelt/table.h

API

class Table {
public:
    struct Cell {
        std::string data;
        color::Color color;
    };
    
    Table(const std::vector<std::string> titles, ssize_t sort_column = 0,
          std::function<bool(const std::string &, const std::string &)> comp = nullptr);
    
    void AddRow(const std::vector<std::string> cells);
    void AddRow(const std::vector<std::string> cells, color::Color color);
    void AddRowWithColors(const std::vector<Cell> cells);
    void AddRow();
    void SetCell(size_t col, Cell &&cell);
    void Print(int width, std::ostream &os);
    void Clear();
    void SortBy(size_t column,
                 std::function<bool(const std::string &, const std::string &)> comp);
    void SortBy(size_t column);
    
    static Cell MakeCell(std::string data, color::Color color = {...});
};

Example

#include "toolbelt/table.h"
#include <iostream>

// Create table with column titles
toolbelt::Table table({"Name", "Age", "City"}, 0);  // Sort by first column

// Add rows
table.AddRow({"Alice", "30", "New York"});
table.AddRow({"Bob", "25", "London"});
table.AddRow({"Charlie", "35", "Paris"});

// Add row with color
auto red = toolbelt::color::Color{.mod = toolbelt::color::kRed};
table.AddRow({"David", "40", "Tokyo"}, red);

// Add row with per-cell colors
std::vector<toolbelt::Table::Cell> cells = {
    toolbelt::Table::MakeCell("Eve", toolbelt::color::Color{.mod = toolbelt::color::kGreen}),
    toolbelt::Table::MakeCell("28"),
    toolbelt::Table::MakeCell("Berlin")
};
table.AddRowWithColors(cells);

// Sort by age column (column 1)
table.SortBy(1, [](const std::string &a, const std::string &b) {
    return std::stoi(a) < std::stoi(b);
});

// Print table
table.Print(80, std::cout);

PayloadBuffer

A memory buffer with built-in allocation and deallocation. Useful for message serialization and wire protocols. Supports both fixed-size and resizable buffers, with optional bitmap-based small block allocator.

Header: toolbelt/payload_buffer.h

Key Types

using BufferOffset = uint32_t;

struct PayloadBuffer {
    // Constructors
    PayloadBuffer(uint32_t size, bool bitmap_allocator = true);
    PayloadBuffer(uint32_t initial_size, Resizer r, bool bitmap_allocator = true);
    
    // Allocation
    static void *Allocate(PayloadBuffer **buffer, uint32_t n,
                         bool clear = true, bool enable_small_block = true);
    void Free(void *p);
    static void *Realloc(PayloadBuffer **buffer, void *p, uint32_t n,
                        bool clear = true, bool enable_small_block = true);
    
    // String operations
    static char *SetString(PayloadBuffer **self, const char *s, size_t len,
                          BufferOffset header_offset);
    std::string GetString(BufferOffset header_offset) const;
    std::string_view GetStringView(BufferOffset header_offset) const;
    
    // Vector operations
    template <typename T>
    static bool VectorPush(PayloadBuffer **self, VectorHeader *hdr, T v,
                          bool enable_small_block = true);
    template <typename T>
    static bool VectorReserve(PayloadBuffer **self, VectorHeader *hdr, size_t n,
                             bool enable_small_block = true);
    template <typename T>
    static bool VectorResize(PayloadBuffer **self, VectorHeader *hdr, size_t n);
    template <typename T>
    T VectorGet(const VectorHeader *hdr, size_t index) const;
    
    // Address conversion
    template <typename T = void>
    T *ToAddress(BufferOffset offset, size_t size = 0);
    template <typename T = void>
    BufferOffset ToOffset(T *addr, size_t size = 0);
    
    // Utilities
    size_t Size() const;
    void Dump(std::ostream &os);
    bool IsValidMagic() const;
    bool IsMoveable() const;
    bool AllocationFailed() const;
};

Fixed-size buffers that run out of space

When a fixed-size buffer has no room for an allocation, the allocating functions report it and leave the buffer's existing contents unchanged: Allocate, Realloc, SetString, AllocateMainMessage and NewMessage return nullptr, AllocateString returns an empty span, and AllocateMetadata, VectorPush, VectorReserve and VectorResize return false. The buffer also records the failure in its magic word (kAllocationFailedFlag), and AllocationFailed() reports it. The writer can keep making smaller allocations, while a reader that checks the magic using kBitMapMask treats the incomplete buffer as invalid.

With bitmaps enabled, a small allocation that has no room for a new bitmap run comes from the free list instead, so a small fixed-size buffer can use all of its space. VectorResize sets the elements it adds to zero.

Example

#include "toolbelt/payload_buffer.h"

// Create a fixed-size buffer
char buffer_memory[4096];
toolbelt::PayloadBuffer *pb = new (buffer_memory) toolbelt::PayloadBuffer(4096);

// Allocate memory in the buffer
void *data = toolbelt::PayloadBuffer::Allocate(&pb, 100);
// Use data...

// Free memory
pb->Free(data);

// Create a resizable buffer
auto resizer = [](toolbelt::PayloadBuffer **b, size_t old_size, size_t new_size) {
    char *new_mem = new char[new_size];
    memcpy(new_mem, *b, old_size);
    delete[] reinterpret_cast<char*>(*b);
    *b = reinterpret_cast<toolbelt::PayloadBuffer*>(new_mem);
};
toolbelt::PayloadBuffer *resizable = new toolbelt::PayloadBuffer(1024, resizer);

// Allocate string
toolbelt::BufferOffset str_offset = 100;
toolbelt::PayloadBuffer::SetString(&resizable, "Hello", 5, str_offset);
std::string str = resizable->GetString(str_offset);

// Vector operations
toolbelt::VectorHeader vec_hdr = {0, 0};
toolbelt::PayloadBuffer::VectorPush<int>(&resizable, &vec_hdr, 42);
toolbelt::PayloadBuffer::VectorPush<int>(&resizable, &vec_hdr, 100);
int value = resizable->VectorGet<int>(&vec_hdr, 0);  // Returns 42

TriggerFd

A file descriptor that can be used to trigger events. Implemented as an eventfd on Linux or a pipe on other systems.

Header: toolbelt/triggerfd.h

API

class TriggerFd {
public:
    TriggerFd();
    TriggerFd(const FileDescriptor &poll_fd, const FileDescriptor &trigger_fd);
    
    absl::Status Open();
    static absl::StatusOr<TriggerFd> Create();
    static absl::StatusOr<TriggerFd> Create(const FileDescriptor &poll_fd,
                                           const FileDescriptor &trigger_fd);
    
    void Close();
    void SetPollFd(FileDescriptor fd);
    void SetTriggerFd(FileDescriptor fd);
    void Trigger();
    bool Clear();  // Clears trigger and returns true if it was triggered
    FileDescriptor &GetPollFd();
    FileDescriptor &GetTriggerFd();
    void AddPollFd(std::vector<struct pollfd> &fds);
};

Example

#include "toolbelt/triggerfd.h"
#include <sys/poll.h>

// Create a trigger file descriptor
auto trigger_result = toolbelt::TriggerFd::Create();
if (!trigger_result.ok()) {
    return;
}
toolbelt::TriggerFd trigger = *trigger_result;

// Add to poll set
std::vector<struct pollfd> fds;
trigger.AddPollFd(fds);

// In event loop
int result = poll(fds.data(), fds.size(), -1);
if (result > 0 && (fds[0].revents & POLLIN)) {
    if (trigger.Clear()) {
        // Trigger was set, handle event
    }
}

// Trigger from another thread/coroutine
trigger.Trigger();

Utility Functions

Now

Get the current monotonic time in nanoseconds.

Header: toolbelt/clock.h

uint64_t Now();

Example

#include "toolbelt/clock.h"

uint64_t start = toolbelt::Now();
// Do work...
uint64_t end = toolbelt::Now();
uint64_t elapsed_ns = end - start;
double elapsed_ms = elapsed_ns / 1e6;

Hexdump

Dump memory in hexadecimal format.

Header: toolbelt/hexdump.h

void Hexdump(const void* addr, size_t length, FILE* out = stdout);

Example

#include "toolbelt/hexdump.h"

char buffer[64] = "Hello, World!";
toolbelt::Hexdump(buffer, sizeof(buffer));
// Output:
// 00000000  48 65 6c 6c 6f 2c 20 57  6f 72 6c 64 21 00 00 00  |Hello, World!...|
// ...

PrintCurrentStack

Print the current stack trace.

Header: toolbelt/stacktrace.h

void PrintCurrentStack(std::ostream &os);

Example

#include "toolbelt/stacktrace.h"
#include <iostream>

void some_function() {
    // Print stack trace
    toolbelt::PrintCurrentStack(std::cerr);
}

Dependencies

  • Abseil - Status types, strings, formatting
  • co - Coroutine library (for socket classes)

License

See LICENSE file for licensing information.

Releases

Packages

Contributors

Languages