Sane C++ Libraries
C++ Platform Abstraction Libraries
SC::Globals Struct Reference

Customizable thread-local and global variables for memory handling. More...

#include <Globals.h>

Public Types

enum  Type {
  Global = 0 ,
  ThreadLocal = 1
}
 

Public Member Functions

 Globals (MemoryAllocator &allocator)
 

Static Public Member Functions

static void init (Type type, GlobalSettings settings={})
 Initialized Globals for current thread. More...
 
static Globalspush (Type type, Globals &globals)
 Sets Globals as current, saving previous one. More...
 
static Globalspop (Type type)
 Restores Globals previously replaced by a push. More...
 
static Globalsget (Type type)
 Obtains current set of Globals. More...
 

Public Attributes

MemoryAllocatorallocator
 

Detailed Description

Customizable thread-local and global variables for memory handling.

This class holds pointers to systems that must be globally available, like the memory allocator. It allows "stacking" different Globals through a push / pop mechanism, connecting them through a linked list. The Default allocator is automatically setup and uses standard malloc, realloc, free for allocations.

Note
Globals use no locking mechanism so they are thread-unsafe. Every method however requires a Globals::Type parameter that can be set to Globals::ThreadLocal to avoid such issues.

Example (Fixed Allocator):

alignas(uint64_t) char stackMemory[256] = {0};
FixedAllocator fixedAllocator = {stackMemory, sizeof(stackMemory)};
Globals globals = {fixedAllocator};
// ...
Buffer& buffer = *Globals::get(Globals::Global).allocator.create<Buffer>();
(void)buffer.append({"ASDF"}); // Allocates from stackMemory
// ...
unsigned long long uint64_t
Platform independent (8) bytes unsigned int.
Definition: PrimitiveTypes.h:42
static Globals * push(Type type, Globals &globals)
Sets Globals as current, saving previous one.
static Globals & get(Type type)
Obtains current set of Globals.
static Globals * pop(Type type)
Restores Globals previously replaced by a push.
@ Global
Shared globals (NOT thread-safe)
Definition: Globals.h:40
T * create(U &&... u)
Allocate and construct an object of type T using this allocator.
Definition: Memory.h:48

Example (Virtual Allocator):

// Create a Virtual memory block that can expand up to 1 MB
VirtualMemory virtualMemory;
SC_TEST_EXPECT(virtualMemory.reserve(1024 * 1024));
VirtualAllocator virtualAllocator = {virtualMemory};
Globals virtualGlobals = {virtualAllocator};
Globals::push(Globals::Global, virtualGlobals);
// ...
Buffer& buffer = *Globals::get(Globals::Global).allocator.create<Buffer>();
(void)buffer.append({"ASDF"}); // Allocates from virtualMemory
// ...
#define SC_TEST_EXPECT(e)
Records a test expectation (eventually aborting or breaking o n failed test)
Definition: Testing.h:116

Example (Memory dump):

// -----------------------------------------------------------------------------
// Example showing how to dump and restore a complex struct to a flat buffer.
// SC::Segment based containers use relative pointers to make this possible.
// DO NOT use this approach when versioning is needed, that means needing to
// de-serialize after adding, removing or moving fields in the structure.
// In such cases consider using SC::SerializationBinary (versioned reflection).
// -----------------------------------------------------------------------------
struct NestedStruct
{
VectorMap<String, int> someMap;
VectorSet<int> someSet;
};
struct ComplexStruct
{
Vector<String> someStrings;
int someField = 0;
String singleString;
NestedStruct nestedStruct;
};
Buffer memoryDump;
// Setup a Virtual Memory allocator with the max upper memory bound
VirtualMemory virtualMemory;
SC_TEST_EXPECT(virtualMemory.reserve(1024 * 1024)); // 1MB is enough here
VirtualAllocator allocator = {virtualMemory};
Globals globals = {allocator};
// Make the allocator current before creating a ComplexStruct
ComplexStruct& object = *allocator.create<ComplexStruct>();
object.someField = 42;
object.singleString = "ASDF";
object.someStrings = {"First", "Second"};
SC_TEST_EXPECT(object.nestedStruct.someSet.insert(213));
SC_TEST_EXPECT(object.nestedStruct.someMap.insertIfNotExists({"1", 1}));
// Save used bytes to memoryDump, checking that one page has been committed
Span<const void> memory = {allocator.data(), allocator.size()};
SC_TEST_EXPECT(virtualMemory.committedBytes == virtualMemory.getPageSize());
SC_TEST_EXPECT(memory.sizeInBytes() < virtualMemory.getPageSize());
SC_TEST_EXPECT(memory.data() == &object);
SC_TEST_EXPECT((size_t(memoryDump.data()) % alignof(ComplexStruct)) == 0);
// Dump AFTER Globals::pop, using default allocator, and release virtual memory
SC_TEST_EXPECT(memoryDump.append(memory));
SC_TEST_EXPECT(virtualMemory.release());
// -----------------------------------------------------------------------------
// Obtain a read-only view over ComplexStruct by re-interpreting the memory dump
// NOTE: There's no need to call ComplexStruct destructor at end of scope
// WARN: start_lifetime_as obtains a ComplexStruct with proper lifetime.
// It works on all tested compilers (debug and release) but it's not technically
// UB-free as ComplexStruct is not implicit-lifetime.
// -----------------------------------------------------------------------------
const Span<const void> span = memoryDump.toSpanConst();
const ComplexStruct& readonly = *span.start_lifetime_as<const ComplexStruct>();
SC_TEST_EXPECT(readonly.someField == 42);
SC_TEST_EXPECT(readonly.singleString == "ASDF");
SC_TEST_EXPECT(readonly.someStrings[0] == "First");
SC_TEST_EXPECT(readonly.someStrings[1] == "Second");
SC_TEST_EXPECT(readonly.someStrings.size() == 2);
SC_TEST_EXPECT(readonly.nestedStruct.someSet.size() == 1);
SC_TEST_EXPECT(readonly.nestedStruct.someSet.contains(213));
SC_TEST_EXPECT(*readonly.nestedStruct.someMap.get("1") == 1);
// -----------------------------------------------------------------------------
// To modify the struct again, copy the read-only view to a new object.
// A Fixed or Virtual allocator can be used here to group sparse allocations in
// a nice single contiguous buffer, before dumping it again to disk or network.
// -----------------------------------------------------------------------------
ComplexStruct modifiable = readonly;
SC_TEST_EXPECT(modifiable.someStrings[0] == "First");
modifiable.someStrings[0] = "First modified";
SC_TEST_EXPECT(modifiable.someStrings[0] == "First modified");

Member Enumeration Documentation

◆ Type

Enumerator
Global 

Shared globals (NOT thread-safe)

ThreadLocal 

Thread-specific globals (separate copy for each thread)

Member Function Documentation

◆ get()

static Globals & SC::Globals::get ( Type  type)
static

Obtains current set of Globals.

◆ init()

static void SC::Globals::init ( Type  type,
GlobalSettings  settings = {} 
)
static

Initialized Globals for current thread.

Note
Each thread can use different GlobalSettings

◆ pop()

static Globals * SC::Globals::pop ( Type  type)
static

Restores Globals previously replaced by a push.

Returns
Pointer to Globals that are no longer current (or nullptr if current is the Default Allocator)

◆ push()

static Globals * SC::Globals::push ( Type  type,
Globals globals 
)
static

Sets Globals as current, saving previous one.

Returns
Pointer to Globals that have been replaced

The documentation for this struct was generated from the following file: