425 lines
14 KiB
C
425 lines
14 KiB
C
/**
|
|
* @file api_defines.h
|
|
* @brief Supporting defines for the public API.
|
|
*
|
|
* This file contains constants / macros that are typically not used by an
|
|
* application but support the public API, and therefore must be exposed. This
|
|
* header should not be included by itself.
|
|
*/
|
|
|
|
#ifndef FLECS_API_DEFINES_H
|
|
#define FLECS_API_DEFINES_H
|
|
|
|
#include "api_flags.h"
|
|
|
|
#if defined(_WIN32) || defined(_MSC_VER)
|
|
#define ECS_TARGET_WINDOWS
|
|
#elif defined(__ANDROID__)
|
|
#define ECS_TARGET_ANDROID
|
|
#define ECS_TARGET_POSIX
|
|
#elif defined(__linux__)
|
|
#define ECS_TARGET_LINUX
|
|
#define ECS_TARGET_POSIX
|
|
#elif defined(__FreeBSD__)
|
|
#define ECS_TARGET_FREEBSD
|
|
#define ECS_TARGET_POSIX
|
|
#elif defined(__APPLE__) && defined(__MACH__)
|
|
#define ECS_TARGET_DARWIN
|
|
#define ECS_TARGET_POSIX
|
|
#elif defined(__EMSCRIPTEN__)
|
|
#define ECS_TARGET_EM
|
|
#define ECS_TARGET_POSIX
|
|
#endif
|
|
|
|
#if defined(__MINGW32__) || defined(__MINGW64__)
|
|
#define ECS_TARGET_MINGW
|
|
#endif
|
|
|
|
#if defined(_MSC_VER)
|
|
#ifndef __clang__
|
|
#define ECS_TARGET_MSVC
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__clang__)
|
|
#define ECS_TARGET_CLANG
|
|
#endif
|
|
|
|
#if defined(__GNUC__)
|
|
#define ECS_TARGET_GNU
|
|
#endif
|
|
|
|
/* Map between clang and apple clang versions, as version 13 has a difference in
|
|
* the format of __PRETTY_FUNCTION__ which enum reflection depends on. */
|
|
#if defined(__clang__)
|
|
#if defined(__APPLE__)
|
|
#if __clang_major__ == 13
|
|
#if __clang_minor__ < 1
|
|
#define ECS_CLANG_VERSION 12
|
|
#else
|
|
#define ECS_CLANG_VERSION 13
|
|
#endif
|
|
#else
|
|
#define ECS_CLANG_VERSION __clang_major__
|
|
#endif
|
|
#else
|
|
#define ECS_CLANG_VERSION __clang_major__
|
|
#endif
|
|
#endif
|
|
|
|
/* Ignored warnings */
|
|
#if defined(ECS_TARGET_CLANG)
|
|
/* Ignore unknown options so we don't have to care about the compiler version */
|
|
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
|
/* Warns for double or redundant semicolons. There are legitimate cases where a
|
|
* semicolon after an empty statement is useful, for example after a macro that
|
|
* is replaced with a code block. With this warning enabled, semicolons would
|
|
* only have to be added after macro's that are not code blocks, which in some
|
|
* cases isn't possible as the implementation of a macro can be different in
|
|
* debug/release mode. */
|
|
#pragma clang diagnostic ignored "-Wextra-semi-stmt"
|
|
/* This is valid in C99, and Flecs must be compiled as C99. */
|
|
#pragma clang diagnostic ignored "-Wdeclaration-after-statement"
|
|
/* Clang attribute to detect fallthrough isn't supported on older versions.
|
|
* Implicit fallthrough is still detected by gcc and ignored with "fall through"
|
|
* comments */
|
|
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
|
|
/* This warning prevents adding a default case when all enum constants are part
|
|
* of the switch. In C however an enum type can assume any value in the range of
|
|
* the type, and this warning makes it harder to catch invalid enum values. */
|
|
#pragma clang diagnostic ignored "-Wcovered-switch-default"
|
|
/* This warning prevents some casts of function results to a different kind of
|
|
* type, e.g. casting an int result to double. Not very useful in practice, as
|
|
* it just forces the code to assign to a variable first, then cast. */
|
|
#pragma clang diagnostic ignored "-Wbad-function-cast"
|
|
/* Format strings can be passed down from other functions. */
|
|
#pragma clang diagnostic ignored "-Wformat-nonliteral"
|
|
/* Useful, but not reliable enough. It can incorrectly flag macro's as unused
|
|
* in standalone builds. */
|
|
#pragma clang diagnostic ignored "-Wunused-macros"
|
|
#if __clang_major__ == 13
|
|
/* clang 13 can throw this warning for a define in ctype.h */
|
|
#pragma clang diagnostic ignored "-Wreserved-identifier"
|
|
#endif
|
|
/* Filenames aren't consistent across targets as they can use different casing
|
|
* (e.g. WinSock2 vs winsock2). */
|
|
#pragma clang diagnostic ignored "-Wnonportable-system-include-path"
|
|
/* Enum reflection relies on testing constant values that may not be valid for
|
|
* the enumeration. */
|
|
#pragma clang diagnostic ignored "-Wenum-constexpr-conversion"
|
|
/* Very difficult to workaround this warning in C, especially for an ECS. */
|
|
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
|
/* This warning gets thrown when trying to cast pointer returned from dlproc */
|
|
#pragma clang diagnostic ignored "-Wcast-function-type-strict"
|
|
/* This warning can get thrown for expressions that evaluate to constants
|
|
* in debug/release mode. */
|
|
#pragma clang diagnostic ignored "-Wconstant-logical-operand"
|
|
#elif defined(ECS_TARGET_GNU)
|
|
#ifndef __cplusplus
|
|
#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
|
|
#pragma GCC diagnostic ignored "-Wbad-function-cast"
|
|
#endif
|
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
|
#pragma GCC diagnostic ignored "-Wunused-macros"
|
|
/* This warning gets thrown *sometimes* when not all members for a struct are
|
|
* provided in an initializer. Flecs heavily relies on descriptor structs that
|
|
* only require partly initialization, so this warning isn't useful.
|
|
* It doesn't introduce any safety issues (fields are guaranteed to be 0
|
|
* initialized), and later versions of gcc (>=11) seem to no longer throw this
|
|
* warning. */
|
|
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
|
#endif
|
|
|
|
/* Standard library dependencies */
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
|
|
/* Non-standard but required. If not provided by platform, add manually. */
|
|
#include <stdint.h>
|
|
|
|
/* Contains macros for importing / exporting symbols */
|
|
#include "../bake_config.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#ifdef __BAKE_LEGACY__
|
|
#define FLECS_LEGACY
|
|
#endif
|
|
|
|
/* Some symbols are only exported when building in debug build, to enable
|
|
* whitebox testing of internal datastructures */
|
|
#ifndef FLECS_NDEBUG
|
|
#define FLECS_DBG_API FLECS_API
|
|
#else
|
|
#define FLECS_DBG_API
|
|
#endif
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// Language support defines
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef FLECS_LEGACY
|
|
#include <stdbool.h>
|
|
#endif
|
|
|
|
#ifndef NULL
|
|
#define NULL ((void*)0)
|
|
#endif
|
|
|
|
/* The API uses the native bool type in C++, or a custom one in C */
|
|
#if !defined(__cplusplus) && !defined(__bool_true_false_are_defined)
|
|
#undef bool
|
|
#undef true
|
|
#undef false
|
|
typedef char bool;
|
|
#define false 0
|
|
#define true !false
|
|
#endif
|
|
|
|
/* Utility types to indicate usage as bitmask */
|
|
typedef uint8_t ecs_flags8_t;
|
|
typedef uint16_t ecs_flags16_t;
|
|
typedef uint32_t ecs_flags32_t;
|
|
typedef uint64_t ecs_flags64_t;
|
|
|
|
/* Keep unsigned integers out of the codebase as they do more harm than good */
|
|
typedef int32_t ecs_size_t;
|
|
|
|
/* Allocator type */
|
|
typedef struct ecs_allocator_t ecs_allocator_t;
|
|
|
|
#define ECS_SIZEOF(T) ECS_CAST(ecs_size_t, sizeof(T))
|
|
|
|
/* Use alignof in C++, or a trick in C. */
|
|
#ifdef __cplusplus
|
|
#define ECS_ALIGNOF(T) static_cast<int64_t>(alignof(T))
|
|
#elif defined(ECS_TARGET_MSVC)
|
|
#define ECS_ALIGNOF(T) (int64_t)__alignof(T)
|
|
#elif defined(ECS_TARGET_GNU)
|
|
#define ECS_ALIGNOF(T) (int64_t)__alignof__(T)
|
|
#else
|
|
#define ECS_ALIGNOF(T) ((int64_t)&((struct { char c; T d; } *)0)->d)
|
|
#endif
|
|
|
|
#ifndef FLECS_NO_DEPRECATED_WARNINGS
|
|
#if defined(ECS_TARGET_GNU)
|
|
#define ECS_DEPRECATED(msg) __attribute__((deprecated(msg)))
|
|
#elif defined(ECS_TARGET_MSVC)
|
|
#define ECS_DEPRECATED(msg) __declspec(deprecated(msg))
|
|
#else
|
|
#define ECS_DEPRECATED(msg)
|
|
#endif
|
|
#else
|
|
#define ECS_DEPRECATED(msg)
|
|
#endif
|
|
|
|
#define ECS_ALIGN(size, alignment) (ecs_size_t)((((((size_t)size) - 1) / ((size_t)alignment)) + 1) * ((size_t)alignment))
|
|
|
|
/* Simple utility for determining the max of two values */
|
|
#define ECS_MAX(a, b) (((a) > (b)) ? a : b)
|
|
#define ECS_MIN(a, b) (((a) < (b)) ? a : b)
|
|
|
|
/* Abstraction on top of C-style casts so that C functions can be used in C++
|
|
* code without producing warnings */
|
|
#ifndef __cplusplus
|
|
#define ECS_CAST(T, V) ((T)(V))
|
|
#else
|
|
#define ECS_CAST(T, V) (static_cast<T>(V))
|
|
#endif
|
|
|
|
/* Utility macro for doing const casts without warnings */
|
|
#ifndef __cplusplus
|
|
#define ECS_CONST_CAST(type, value) ((type)(uintptr_t)(value))
|
|
#else
|
|
#define ECS_CONST_CAST(type, value) (const_cast<type>(value))
|
|
#endif
|
|
|
|
/* Utility macro for doing pointer casts without warnings */
|
|
#ifndef __cplusplus
|
|
#define ECS_PTR_CAST(type, value) ((type)(uintptr_t)(value))
|
|
#else
|
|
#define ECS_PTR_CAST(type, value) (reinterpret_cast<type>(value))
|
|
#endif
|
|
|
|
/* Utility macro's to do bitwise comparisons between floats without warnings */
|
|
#define ECS_EQ(a, b) (ecs_os_memcmp(&(a), &(b), sizeof(a)) == 0)
|
|
#define ECS_NEQ(a, b) (!ECS_EQ(a, b))
|
|
#define ECS_EQZERO(a) ECS_EQ(a, (uint64_t){0})
|
|
#define ECS_NEQZERO(a) ECS_NEQ(a, (uint64_t){0})
|
|
|
|
#define ECS_CONCAT(a, b) a ## b
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// Magic numbers for sanity checking
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/* Magic number to identify the type of the object */
|
|
#define ecs_world_t_magic (0x65637377)
|
|
#define ecs_stage_t_magic (0x65637373)
|
|
#define ecs_query_t_magic (0x65637371)
|
|
#define ecs_rule_t_magic (0x65637375)
|
|
#define ecs_table_t_magic (0x65637374)
|
|
#define ecs_filter_t_magic (0x65637366)
|
|
#define ecs_trigger_t_magic (0x65637372)
|
|
#define ecs_observer_t_magic (0x65637362)
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// Entity id macros
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define ECS_ROW_MASK (0x0FFFFFFFu)
|
|
#define ECS_ROW_FLAGS_MASK (~ECS_ROW_MASK)
|
|
#define ECS_RECORD_TO_ROW(v) (ECS_CAST(int32_t, (ECS_CAST(uint32_t, v) & ECS_ROW_MASK)))
|
|
#define ECS_RECORD_TO_ROW_FLAGS(v) (ECS_CAST(uint32_t, v) & ECS_ROW_FLAGS_MASK)
|
|
#define ECS_ROW_TO_RECORD(row, flags) (ECS_CAST(uint32_t, (ECS_CAST(uint32_t, row) | (flags))))
|
|
|
|
#define ECS_ID_FLAGS_MASK (0xFFull << 60)
|
|
#define ECS_ENTITY_MASK (0xFFFFFFFFull)
|
|
#define ECS_GENERATION_MASK (0xFFFFull << 32)
|
|
#define ECS_GENERATION(e) ((e & ECS_GENERATION_MASK) >> 32)
|
|
#define ECS_GENERATION_INC(e) ((e & ~ECS_GENERATION_MASK) | ((0xFFFF & (ECS_GENERATION(e) + 1)) << 32))
|
|
#define ECS_COMPONENT_MASK (~ECS_ID_FLAGS_MASK)
|
|
#define ECS_HAS_ID_FLAG(e, flag) ((e) & ECS_##flag)
|
|
#define ECS_IS_PAIR(id) (((id) & ECS_ID_FLAGS_MASK) == ECS_PAIR)
|
|
#define ECS_PAIR_FIRST(e) (ecs_entity_t_hi(e & ECS_COMPONENT_MASK))
|
|
#define ECS_PAIR_SECOND(e) (ecs_entity_t_lo(e))
|
|
#define ECS_HAS_RELATION(e, rel) (ECS_HAS_ID_FLAG(e, PAIR) && (ECS_PAIR_FIRST(e) == rel))
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// Convert between C typenames and variables
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** Translate C type to id. */
|
|
#define ecs_id(T) FLECS_ID##T##ID_
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// Utilities for working with pair identifiers
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define ecs_entity_t_lo(value) ECS_CAST(uint32_t, value)
|
|
#define ecs_entity_t_hi(value) ECS_CAST(uint32_t, (value) >> 32)
|
|
#define ecs_entity_t_comb(lo, hi) ((ECS_CAST(uint64_t, hi) << 32) + ECS_CAST(uint32_t, lo))
|
|
|
|
#define ecs_pair(pred, obj) (ECS_PAIR | ecs_entity_t_comb(obj, pred))
|
|
#define ecs_pair_t(pred, obj) (ECS_PAIR | ecs_entity_t_comb(obj, ecs_id(pred)))
|
|
#define ecs_pair_first(world, pair) ecs_get_alive(world, ECS_PAIR_FIRST(pair))
|
|
#define ecs_pair_second(world, pair) ecs_get_alive(world, ECS_PAIR_SECOND(pair))
|
|
#define ecs_pair_relation ecs_pair_first
|
|
#define ecs_pair_object ecs_pair_second
|
|
|
|
#define ecs_poly_id(tag) ecs_pair(ecs_id(EcsPoly), tag)
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// Debug macros
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef FLECS_NDEBUG
|
|
#define ECS_TABLE_LOCK(world, table) ecs_table_lock(world, table)
|
|
#define ECS_TABLE_UNLOCK(world, table) ecs_table_unlock(world, table)
|
|
#else
|
|
#define ECS_TABLE_LOCK(world, table)
|
|
#define ECS_TABLE_UNLOCK(world, table)
|
|
#endif
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// Actions that drive iteration
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define EcsIterNextYield (0) /* Move to next table, yield current */
|
|
#define EcsIterYield (-1) /* Stay on current table, yield */
|
|
#define EcsIterNext (1) /* Move to next table, don't yield */
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// Convenience macros for ctor, dtor, move and copy
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef FLECS_LEGACY
|
|
|
|
/* Constructor/Destructor convenience macro */
|
|
#define ECS_XTOR_IMPL(type, postfix, var, ...)\
|
|
void type##_##postfix(\
|
|
void *_ptr,\
|
|
int32_t _count,\
|
|
const ecs_type_info_t *type_info)\
|
|
{\
|
|
(void)_ptr;\
|
|
(void)_count;\
|
|
(void)type_info;\
|
|
for (int32_t i = 0; i < _count; i ++) {\
|
|
type *var = &((type*)_ptr)[i];\
|
|
(void)var;\
|
|
__VA_ARGS__\
|
|
}\
|
|
}
|
|
|
|
/* Copy convenience macro */
|
|
#define ECS_COPY_IMPL(type, dst_var, src_var, ...)\
|
|
void type##_##copy(\
|
|
void *_dst_ptr,\
|
|
const void *_src_ptr,\
|
|
int32_t _count,\
|
|
const ecs_type_info_t *type_info)\
|
|
{\
|
|
(void)_dst_ptr;\
|
|
(void)_src_ptr;\
|
|
(void)_count;\
|
|
(void)type_info;\
|
|
for (int32_t i = 0; i < _count; i ++) {\
|
|
type *dst_var = &((type*)_dst_ptr)[i];\
|
|
const type *src_var = &((const type*)_src_ptr)[i];\
|
|
(void)dst_var;\
|
|
(void)src_var;\
|
|
__VA_ARGS__\
|
|
}\
|
|
}
|
|
|
|
/* Move convenience macro */
|
|
#define ECS_MOVE_IMPL(type, dst_var, src_var, ...)\
|
|
void type##_##move(\
|
|
void *_dst_ptr,\
|
|
void *_src_ptr,\
|
|
int32_t _count,\
|
|
const ecs_type_info_t *type_info)\
|
|
{\
|
|
(void)_dst_ptr;\
|
|
(void)_src_ptr;\
|
|
(void)_count;\
|
|
(void)type_info;\
|
|
for (int32_t i = 0; i < _count; i ++) {\
|
|
type *dst_var = &((type*)_dst_ptr)[i];\
|
|
type *src_var = &((type*)_src_ptr)[i];\
|
|
(void)dst_var;\
|
|
(void)src_var;\
|
|
__VA_ARGS__\
|
|
}\
|
|
}
|
|
|
|
#define ECS_HOOK_IMPL(type, func, var, ...)\
|
|
void func(ecs_iter_t *_it)\
|
|
{\
|
|
for (int32_t i = 0; i < _it->count; i ++) {\
|
|
ecs_entity_t entity = _it->entities[i];\
|
|
type *var = &((type*)_it->ptrs[0])[i];\
|
|
(void)entity;\
|
|
(void)var;\
|
|
__VA_ARGS__\
|
|
}\
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|