🟩 Primitive types, asserts, limits, Function, Span, Result, Tagged Union
Foundation library provides many fundamental type definitions and types widely used by other libraries.
As this is included and needed by almost every other library, it tries to keep bloat to the bare minimum.
Detailed documentation is in the Foundation topic.
Features
Classes
Class | Description |
SC::Span | View over a contiguous sequence of items (pointer + size in elements). |
SC::Result | An ascii string used as boolean result. SC_TRY macro forwards errors to caller. |
SC::Function | Wraps function pointers, member functions and lambdas without ever allocating. |
SC::Deferred | Executes a function at end of current scope (in the spirit of Zig defer keyword). |
SC::OpaqueObject | Hides implementation details from public headers (static PIMPL). |
SC::TaggedUnion | Type safe union with an enum type, where each type has an associated enum value. |
SC::UniqueHandle | Move only handle that has a special tag value flagging its invalid state. |
Macros
- Compiler Macros: Compiler Macros Preprocessor macros to detect compiler and platform features.
Type Traits
- Type Traits: Type Traits (EnableIf, AddPointer, RemovePointer, etc.)
Utilities
Class | Description |
SC::Assert | Functions and macros to assert, exit() or abort() and capture backtraces. |
SC::AlignedStorage | A buffer of bytes with given alignment. |
SC::MaxValue | An object that can be converted to any primitive type providing its max value. |
SC::Memory | Centralized functions to allocate, reallocate and deallocate memory. |
Status
🟩 Usable
The library is very simple it it has what is needed so far by the other libraries.
Description
There is an hard rule in the library Principles not to include system and compiler headers in public headers.
Foundation provides all primitive types to be used in headers and classes like SC::UniqueHandle, SC::OpaqueObject, SC::AlignedStorage to encourage static PIMPL in order to hide platform specific implementation details everywhere.
Function
Wraps function pointers, member functions and lambdas without ever allocating.
Example:
struct MyClass
{
float memberValue = 2.0;
int memberFunc(float a) { return static_cast<int>(a + memberValue); }
};
int someFunc(float a) { return static_cast<int>(a * 2); }
struct BigClass
{
};
MyClass myClass;
Function<int(float)> func;
func = &someFunc;
func.bind<MyClass, &MyClass::memberFunc>(myClass);
func = [](float a) -> int { return static_cast<int>(a + 1.5); };
BigClass bigClass;
unsigned long long uint64_t
Platform independent (8) bytes unsigned int.
Definition: PrimitiveTypes.h:42
Size of lambdas or less than LAMBDA_SIZE (currently 2 * sizeof(void*)
).
If lambda is bigger than LAMBDA_SIZE
the constructor will static assert.
- Template Parameters
-
FuncType | Type of function to be wrapped (Lambda, free function or pointer to member function) |
Deferred
Executes a function at end of current scope (in the spirit of Zig defer
keyword).
- Template Parameters
-
F | The lambda / function to execute |
Example:
HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_DUP_HANDLE, FALSE, processId);
if (processHandle == nullptr)
{
return false;
}
[&]
{
CloseHandle(processHandle);
processHandle = nullptr;
});
deferDeleteProcessHandle.disarm()
Deferred< F > MakeDeferred(F &&f)
Creates a Deferred object holding a function that will be invoked at end of current scope.
Definition: Deferred.h:61
TaggedUnion
Type safe union with an enum type, where each type has an associated enum value.
- Template Parameters
-
Union | with FieldTypes = TypeList<TaggedType<EnumType, EnumValue, Type>, ...> |
Example:
namespace SC
{
struct TaggedUnionTest;
enum TestType
{
TypeString = 10,
TypeInt = 110,
};
struct TestUnion
{
template <TestType E, typename T>
using Tag = TaggedType<TestType, E, T>;
using FieldsTypes = TypeTraits::TypeList<
Tag<TypeString, String>,
Tag<TypeInt, int>>;
};
void taggedUnionUsageSnippet(Console& console)
{
TaggedUnion<TestUnion> test;
String* ptr = test.field<TypeString>();
if (ptr)
{
*ptr = "SomeValue";
}
test.changeTo<TypeInt>() = 2;
switch (test.getType())
{
case TypeString: console.print("String = {}", *test.field<TypeString>()); break;
case TypeInt: console.print("Int = {}", *test.field<TypeInt>()); break;
}
test.setType(TypeString);
*test.field<TypeString>() = "Some new string";
}
}
OpaqueObject
Hides implementation details from public headers (static PIMPL).
Opaque object avoids the heap allocation that often comes with PIMPL, allowing to hide OS specific details from public headers. User declares size in bytes of a struct in the header but the structure can be defined in an implementation .cpp file. Choosing a size that is too small will generate a static_assert
that contains in the error message the minimum size to use. Up to 4 functions will need to be defined to avoid linker errors (construct
, destruct
, moveConstruct
, moveAssign
). These functions are meant to be defined in a .cpp
file that will know how to construct Object, as it can see its definition.
- Template Parameters
-
Definition | Pass in a custom Definition declaring Sizes and alignment on different platforms |
Example:
struct TestObject
{
private:
struct Internal;
struct InternalDefinition
{
static constexpr int Windows = 224;
static constexpr int Apple = 144;
static constexpr int Default = sizeof(void*);
static constexpr size_t Alignment = alignof(void*);
using Object = Internal;
};
public:
using InternalOpaque = OpaqueObject<InternalDefinition>;
private:
InternalOpaque internal;
};
struct SC::TestObject::Internal
{
SC_NtSetInformationFile pNtSetInformationFile = nullptr;
LPFN_CONNECTEX pConnectEx = nullptr;
LPFN_ACCEPTEX pAcceptEx = nullptr;
LPFN_DISCONNECTEX pDisconnectEx = nullptr;
};
template <>
void SC::TestObject::InternalOpaque::construct(Handle& buffer)
{
placementNew(buffer.reinterpret_as<Object>());
}
template <>
void SC::TestObject::InternalOpaque::destruct(Object& obj)
{
obj.~Object();
}
UniqueHandle
Move only handle that has a special tag value flagging its invalid state.
Typically used to wrap Operating System specific handles.
- Template Parameters
-
Definition | A struct declaring handle type, release function and invalid handle value. |
Example:
struct PosixFileDescriptorDefinition
{
using Handle = int;
static Result releaseHandle(Handle& handle);
static constexpr Handle Invalid = -1;
};
using PosixFileDescriptor = UniqueHandle<PosixFileDescriptorDefinition>;
Result PosixFileDescriptorDefinition::releaseHandle(Handle& handle)
{
if (::close(handle) != 0)
{
return Result::Error("releaseHandle - close failed");
}
return Result(true);
}
PosixFileDescriptor myDescriptor;
const int nativeFd = ::open(filePath.getNullTerminatedNative(), flags, access);
myDescriptor.assign(nativeFd);
PosixFileDescriptor otherDescriptor =
move(myDescriptor);
otherDescriptor.close()
otherDescriptor.detach()
if(otherDescriptor.isValid())
{
}
constexpr T && move(T &value)
Converts an lvalue to an rvalue reference.
Definition: Compiler.h:269
Roadmap
🟦 Complete Features:
- Things will be added as needed
💡 Unplanned Features:
- Note
- In Principles there is a rule that discourages allocations of large number of tiny objects and also creating systems with unclear or shared memory ownership. For this reason this library is missing Smart Pointers.