🟩 Primitive types, asserts, compiler macros, Function, Span, Result
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.
Dependencies
- Direct dependencies: (none)
- All dependencies: (none)
Statistics
- Lines of code (excluding comments): 1215
- Lines of code (including comments): 1842
Features
Classes
Class | Description |
SC::Span | View over a contiguous sequence of items (pointer + size in elements). |
SC::StringSpan | An read-only view over a string (to avoid including Strings library when parsing is not needed). |
SC::StringPath | Pre-sized char array holding enough space to represent a file system path. |
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::UniqueHandle | Move only handle that has a special tag value flagging its invalid state. |
Macros
Compiler Macros Preprocessor macros to detect compiler and platform features.
Type Traits
Type Traits (EnableIf, AddPointer, 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. |
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.
- Template Parameters
-
FuncType | Type of function to be wrapped (Lambda, free function or pointer to member function) |
- Note
- Size of lambdas or less than LAMBDA_SIZE (currently
2 * sizeof(void*)
).
If lambda is bigger than LAMBDA_SIZE
the constructor will static assert.
Example:
struct SomeClass
{
float memberValue = 2.0;
int memberFunc(float a) { return static_cast<int>(a + memberValue); }
};
struct SomeFunctor
{
float memberValue = 2.0;
int operator()(float a) { return static_cast<int>(a + memberValue); }
};
int someFunc(float a) { return static_cast<int>(a * 2); }
struct BigClass
{
};
void SC::FunctionTest::functionDocumentationSnippet()
{
SomeClass someClass;
func = &someFunc;
func.bind<SomeClass, &SomeClass::memberFunc>(someClass);
func = [](float a) -> int { return static_cast<int>(a + 1.5); };
func = SomeFunctor{2.5};
}
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;
});
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:
... in the header file
struct FileSystemWatcher
{
private:
struct Internal;
struct InternalDefinition
{
static constexpr int Windows = 3 * sizeof(void*);
static constexpr int Apple = 43 * sizeof(void*) + sizeof(Mutex);
static constexpr int Linux = sizeof(void*) * 4;
static constexpr int Default = Linux;
static constexpr size_t Alignment = alignof(void*);
using Object = Internal;
};
public:
using InternalOpaque = OpaqueObject<InternalDefinition>;
private:
InternalOpaque internal;
... in .cpp file Declare Internal struct with all platform specific details (requiring OS specific headers).
#include "../../FileSystemWatcher/FileSystemWatcher.h"
#include "../../Foundation/Deferred.h"
#include "../../Threading/Atomic.h"
#include "../../Threading/Threading.h"
#include <CoreServices/CoreServices.h>
#include <AvailabilityMacros.h>
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#include "FSEventsIOS.h"
#endif
struct SC::FileSystemWatcher::Internal
{
FileSystemWatcher* self = nullptr;
CFRunLoopRef runLoop = nullptr;
CFRunLoopSourceRef refreshSignal = nullptr;
FSEventStreamRef fsEventStream = nullptr;
Thread pollingThread;
Result signalReturnCode = Result(false);
EventObject refreshSignalFinished;
Mutex mutex;
EventLoopRunner* eventLoopRunner = nullptr;
Notification notification;
FolderWatcher* watcher;
Atomic<bool> closing = false;
Declare only two of the four functions to avoid linker errors.
template <>
void SC::FileSystemWatcher::InternalOpaque::construct(Handle& buffer)
{
placementNew(buffer.reinterpret_as<Object>());
}
template <>
void SC::FileSystemWatcher::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:
... definition in header
struct SC_COMPILER_EXPORT FileDescriptorDefinition
{
using Handle = int;
static Result releaseHandle(Handle& handle);
static constexpr Handle Invalid = -1;
};
... derive from it
struct SC_COMPILER_EXPORT FileDescriptor : public UniqueHandle<detail::FileDescriptorDefinition>
{
using UniqueHandle::UniqueHandle;
...declaration in .cpp file
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
SC::Result SC::detail::FileDescriptorDefinition::releaseHandle(Handle& handle)
{
if (::close(handle) != 0)
{
return Result::Error(
"FileDescriptorDefinition::releaseHandle - close failed");
}
return Result(true);
}
...usage
#include <fcntl.h>
{
SC_TRY(filePath.path.assign(
"someFile.txt"));
const int flags = O_RDWR | O_CREAT | O_TRUNC;
const int access = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
const int nativeFd = ::open(filePath.path.buffer, flags, access);
SC_TRY(myDescriptor.assign(nativeFd));
SC_TRY(otherDescriptor.close());
otherDescriptor.detach();
if (otherDescriptor.isValid())
{
}
}
Blog
Some relevant blog posts are:
Roadmap
🟦 Complete Features:
- Things will be added as needed