CMT is a modular, portable C library extension that fills in gaps left by the standard C library.
It is fully compatible with all major compilers and platforms.
git clone --recurse-submodules --depth 1 "git@github.com:TeomanDeniz/CMT.git"Cloning with --depth 1 avoids downloading the full repository history.
Place the cloned CMT folder inside your project and include the header:
#include "CMT/CMT.H"CMT is header-only. No additional setup required.
To include all features:
#include "CMT/CMT.H" // For C
// or
#include "CMT/CMT.HPP" // For C++You can selectively include specific components by defining INCL__<MODULE> macros before the include:
#define INCL__FAR
#include "CMT/CMT.H" // Includes only the FAR moduleOr
#define INCL__INLINE
#define INCL__OBJECT
#include "CMT/CMT.H" // Includes INLINE and OBJECT modules onlyYou can selectively include a specific whole section by defining INCL__<SECTION> macros before the include:
#define INCL__KEYWORDS
#include "CMT/CMT.H" // Includes everything in KEYWORDSor
#define INCL__ATTRIBUTES
#define INCL__KEYWORDS
#define INCL__RAX
#include "CMT/CMT.H" // Includes RAX, All KEYWORDS, and All ATTRIBUTESSo far, we have these macros for including sections:
INCL__ASMINCL__ATTRIBUTESINCL__CHECK_FEATUREINCL__ENVIRONMENTSINCL__FUNCTIONSINCL__KEYWORDS
If no INCL__... macro is defined, all modules will be automatically included by default.
Note: Defining an INCL__<MODULE> that belongs to a section already included via INCL__<SECTION> will not produce any errors or warnings.
An example script that made by using this library Tested and worked on both Win2000 GCC Version 3.2, Debian-6 32-bit GCC, Debian-13 64-bit GCC and Win11 GCC 8.1.0:
#include <stdio.h> /* For printf() */
#define INCL__OBJECT
#define INCL__TRY_CATCH
#define INCL__TYPES
#include "CMT/CMT.H"
// User MUST use different commands depends on the CPU type.
// For architectures, RCX or RAX like registers automatically downs to ECX or EAX but no alterantive exist,
// compiler will throw an error when an unexisting command is used on a certain CPU architecture, type, or version.
#include "CMT/ASM/PUSH.H" // Works on ALL compilers who supports inline assembly without an error or warning by using inline assembly.
extern var ASD(var, var);
#if defined(__unix__)
SECTION (ASD)
ADD64 (RDI, RSI)
MOV64 (RAX, RDI)
RET
END
#elif defined(_WIN64)
SECTION (ASD)
ADD64 (RCX, RDX)
MOV64 (RAX, RCX)
RET
END
#endif
extern var return_42(void);
SECTION (return_42)
MOV64 (RAX, VALUE(42))
RET
END
#include "CMT/ASM/POP.H"object o_class // Can be also "struct o_class" if you want
{
i_am_an_object; // Necessary and must be at the top
void (*print)();
void (*add)(bit32);
bit32 value;
};
void print()
{
object__connect (o_class); // Needed for defining "this"
printf("[%d]\n", this->value);
throw (this->value); // Will do nothing if it's not inside a try {...};
}
void add_class(bit32 value)
{
OBJECT__CONNECT (o_class);
THIS->value += value;
}
void o_class(bit32 start_value) // Constructor. Automatically works if object is created
{
object__connect (o_class);
object__inject_2 (add, add_class); // this->add = add_class
object__inject (print); // As you see, manual injection is needed for per function in the object.
// Because, You may need to detect if a needed DLL file exist, and then get the needed function from it and use it in here.
// Otherwise, you can just use your default function.
this->value = start_value;
}int main(void)
{
new (o_class, asd) (42);
new (o_class, asd2) (0);
/*
or instead, you can also do:
#define my_class(object_name) new (o_class, object_name)
so you can use it as:
my_class (asd) (42);
which works like namespace in C++ or smthing idk
*/
asd.add(33); // asd.value is now 84
try
{
int error;
try
{
asd.print();
}
catch (error)
{
printf("ERROR_1: %d\n", error);
throw (-2);
}
}
catch (int error2) // May create error on older compilers, so create error2 at the top like I did in the top try-catch
{
printf("ERROR_2: %d\n", error2);
}
asd2.print();
destroy (asd);
destroy (asd2); // destroys are needed
var test = 42;
printf("ASD: %d\n", ASD(test, 12));
printf("return_42: %d\n", return_42());
return (0);
}
OBJECT - Implements basic OOP (Object-Oriented Programming) features in C.
β οΈ ImportantFile at: [π CMT/OBJECT.H]
#define INCL__OBJECT #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
NEW, new |
#define() |
Create an object |
DESTROY, destroy |
#define() |
Destroy an object (it is necessary) |
OBJECT, object |
#define |
Struct definition |
OBJECT__READY, object__ready |
#define() |
Check if object is successfully allocated; returns (int)1 if true |
I_AM_AN_OBJECT, i_am_an_object |
#define |
Declares the function pointer storage for an object |
OBJECT__CONNECT, object__connect |
#define() |
Connect a structure into a function |
OBJECT__INJECT, object__inject |
#define() |
Inject a function pointer |
OBJECT__INJECT_2, object__inject_2 |
#define() |
Inject a function pointer with a function (custom binding) |
OBJECT__INJECT_3, object__inject_3 |
#define() |
Inject a function pointer with a function in an object |
OBJECT is a high-performance C/C++ library for object-oriented programming in pure C, providing dynamic function tables, flexible object creation, and optimized cross-platform function injection.
This table is for really old compilers. If you're using a modern compiler, ignore this table.
| Compiler & Target | Link / Build Command |
|---|---|
| GCC (Win2000) 3.0 | gcc #.c C:\WINNT\system32\kernel32.dll |
| GCC (Win98) 3.0 | gcc #.c C:\WINNT\system32\kernel32.dll |
| GCC (Win95) 3.0 | gcc #.c C:\WINNT\system32\kernel32.dll |
| GCC (DOS) 3.0? | gcc #.c -ldjgpp |
- Pure C object-oriented programming support
- Cleaner syntax than typical C++ alternatives
- Assembly-level function injection for performance optimizations
- Supports 32-bit and 64-bit systems (likely 16-bit as well)
- Compatible with TI CGT/CCS, GCC, and MSVC compilers
- Dynamic function tables for manual runtime binding
Performance Uses inline/assembly injection to directly inject functions into objects.
Flexibility Dynamically bind functions at runtime without virtual tables.
Portability Supports multiple CPU architectures and OS environments.
Memory efficiency Function tables are allocated once per object.
The setup section is optional if you are compiling for ARM 32-bit and never include this addon in a C file that has a
main()function.Otherwise, skip setup and jump to Contents at the bottom. You don't have to do Setup process.
If a source file does not contain main(), and you still use the library, then one source file must define CMT_SETUP.
This ensures that any global variables or link-exposed functions are properly defined.
After doing this once, you can include this header anywhere else without defining the macro again; other files will only see the extern declarations.
Example:
#define CMT_SETUP
#include "CMT/OBJECT.H"Or
#define CMT_SETUP
#define INCL__OBJECT
#include "CMT/CMT.H"It specifies that the structure you create is an object.
It performs the same function as "struct" and makes no performance changes.
It simply works as
#define object struct.Use it like:
object o_test { i_am_an_object; ... };
Instantiates an object and runs object's constructor.
After you created the object with
new, don't forget to delete it withdestroy.Usage:
new (o_object_type, ObjectName) (constructor, parameters, if_have, any); ObjectName.call_a_function(42); destroy (ObjectName);Suggested macro for ergonomics:
#define test(OBJECT_VAR) new (o_test, OBJECT_VAR) test (obj) (...);
Clears the memory of hte created object if object is successfully created.
Usage:
new (o_object_type, ObjectName) (constructor, parameters, if_have, any); ObjectName.call_a_function(42); destroy (ObjectName);
Declares the function pointer storage for an object. Place at top of the struct.
object o_test { i_am_an_object; ... };
Connects an object structure pointer (
this) to its instance. Put this macro at the top of function bodies that usethis.void function_1() { object__connect(o_object); // this ... }Or, if you use uppercase version, you'll need to use:
void function_1() { OBJECT__CONNECT(o_object); // THIS ... }
Checks if object is properly allocated.
new(test_object_type, OBJ) (); if (object__ready(OBJ)) { OBJ.FUNC1(); OBJ.FUNC2(); destroy(OBJ); } else { printf("Object not ready!\n"); }
Injects a function with same name.
void test_object_type() { object__connect(test_object_type); object__inject(FUNC_A); object__inject(FUNC_B); }Means:
this->FUNC_A = FUNC_A; this->FUNC_B = FUNC_B;
Injects a function with a different name.
void test_object_type() { object__connect(test_object_type); object__inject_2(FUNC_A, impl_func_a); object__inject_2(FUNC_B, impl_func_b); }Means:
this->FUNC_A = impl_func_a; this->FUNC_B = impl_func_b;
Inject a function pointer with a function into a specific object instance.
void test_object_type() { object__connect(test_object_type); object__inject_3(this, FUNC_A, impl_func_a); object__inject_3(this, FUNC_B, impl_func_b); }Means:
this->FUNC_A = impl_func_a; this->FUNC_B = impl_func_b;
Basic object definition
object test_object_type {
i_am_an_object;
void (*worked)(int);
int value;
};Functions
extern void worked(int);
static void test_object_type(void) { // Constructor
object__connect(test_object_type);
object__inject(worked);
this->value = 0;
}
void worked(int n) {
OBJECT__CONNECT(test_object_type);
THIS->value += n;
}Usage
new (test_object_type, obj) (); // constructor called
obj.worked(42); // uses implicit `this`
destroy (obj);Multiple objects example
new (test_object_type, test1) ();
new (test_object_type, test2) ();
test1.worked(42);
test1.worked(42);
test2.worked(42);
test1.worked(42);
test2.worked(42);
destroy (test1);
destroy (test2);Full example (compact)
Type + functions + table
object test_object_type {
i_am_an_object;
int (*FUNC1)();
void (*FUNC2)();
void (*FUNC3)();
int a;
};
static int O_FUNC1() {
object__connect (test_object_type);
printf("1\n");
return (42);
}
static void O_FUNC2() {
object__connect (test_object_type);
printf("2\n");
}
static void FUNC3() {
object__connect (test_object_type);
printf("3\n");
}
static void test_object_type() { // Constructor
object__connect (test_object_type);
object__inject_2 (FUNC1, O_FUNC1);
object__inject_2 (FUNC2, O_FUNC2);
object__inject (FUNC3);
printf("0\n");
}Using objects in main
int main(void) {
{
new (test_object_type, TEST) ();
TEST.FUNC1();
TEST.FUNC2();
TEST.FUNC3();
destroy (TEST);
}
{
new (test_object_type, TEST1) ();
new (test_object_type, TEST2) ();
TEST1.FUNC1();
TEST1.FUNC2();
TEST2.FUNC1();
TEST2.FUNC2();
TEST1.FUNC1();
TEST2.FUNC1();
printf("%d\n", TEST1.FUNC1());
destroy (TEST1);
destroy (TEST2);
}
return (0);
}- Always terminate
object__table(...)with0. - Index 0 of the function table is the constructor.
- Use
object__connect(TYPE)in every function that needsSELF. __OBJECT_MAX_FUNCTION_LIMIT__controls the maximum number of functions per object-increase if required.
PUSH & POP - Use ASM inside C Language (WIP)
[!WARNING]
THIS IS A WIP CONTENT!!! THIS EXTENSION MIGHT NOT WORK ON ALL COMPILERS, OPERATING SYSTEMS, OR ARCHITECTURES!!! MAJOR MAINTENANCE IS PLANNED! USE IT AT YOUR OWN RISK
β οΈ ImportantFile(s) at: [π CMT/ASM/PUSH.H] [π CMT/ASM/POP.H]
#include "CMT/ASM/PUSH.H" // ASM CODE ... #include "CMT/ASM/POP.H"
| Name | Type | Description |
|---|---|---|
SECTION |
#define () |
Create a section. |
END |
#define |
End of section. |
VALUE |
#define () |
Use a direct value. |
MEM8 |
#define () |
Use 8-bit memory (One arg) |
MEM16 |
#define () |
Use 16-bit memory (One arg) |
MEM32 |
#define () |
Use 32-bit memory (One arg) |
MEM64 |
#define () |
Use 64-bit memory (One arg) |
MEM8_INDEX |
#define () |
Use 8-bit memory (Two args) |
MEM16_INDEX |
#define () |
Use 16-bit memory (Two args) |
MEM32_INDEX |
#define () |
Use 32-bit memory (Two args) |
MEM64_INDEX |
#define () |
Use 64-bit memory (Two args) |
Registers:
| Name | Description |
|---|---|
RSI, ESI, SI, AH |
Source index register |
RDI, EDI, DI, BH |
Destination index register |
RBP, EBP, BP, CH |
Base pointer register |
RSP, ESP, SP, DH |
Stack pointer register |
RAX, EAX, AX, AL |
Primary accumulator register |
RBX, EBX, BX, BL |
Base register |
RCX, ECX, CX, CL |
Counter register |
RDX, EDX, DX, DL |
Data register |
CS, DS, SS, ES, FS, GS |
Segment registers |
R8, R9, ..., R14, R15 |
General-purpose extension registers |
CR0, CR2, CR3, CR4, CR8 |
Control registers |
DR0, DR1, ..., DR7, DR8 |
Debug registers |
GDTR, IDTR, LDTR, TR |
Descriptor table registers |
MM0, MM1, ..., MM6, MM7 |
64-bit SIMD registers |
XMM0, XMM1, ..., XMM14, XMM15 |
128-bit registers |
YMM0, YMM1, ..., YMM14, YMM15 |
256-bit registers |
ZMM0, ZMM1, ..., ZMM30, ZMM31 |
512-bit registers |
Commands:
| Name | Type | Description |
|---|---|---|
RET |
#define |
If section called via CALL, jumps back to where it is called. |
MOV8 |
#define () |
Moves 1 byte from the source to the destination. |
MOV16 |
#define () |
Moves 2 Bytes from the source to the destination. |
MOV32 |
#define () |
Moves 4 Bytes from the source to the destination. |
MOV64 |
#define () |
Moves 8 Bytes from the source to the destination. |
ADD8 |
#define () |
Adds 1 byte from the source to the destination. |
ADD16 |
#define () |
Adds 2 bytes from the source to the destination. |
ADD32 |
#define () |
Adds 4 bytes from the source to the destination. |
ADD64 |
#define () |
Adds 8 bytes from the source to the destination. |
Note: MOV64, ADD64, @64 etc... downscale automatically on 32/16-bit modes.
This header provides a unified macro layer that lets you write inline assembly with minimal compiler-specific boilerplate. The system:
- Detects compiler ASM support
- Detects architecture: 16-bit / 32-bit / 64-bit
- Detects backend syntax: AT&T, Intel, or MSVC inline asm
- Defines stable macro keywords so your ASM blocks stay portable
It abstracts:
- Register names
- Memory addressing
- Basic arithmetic and move operations
- Section creation (labels)
- Return instructions
- Ensures your inline ASM can compile across:
-
- GCC / Clang (AT&T syntax)
-
- ICC (AT&T or Intel depending on build)
-
- MSVC (__asm{} Intel syntax)
- Provides stable names for registers that change size across architectures
- Allows macro-style 8/16/32/64-bit ops without manual syntax branching
- Provides unified memory addressing patterns
- Enables platform-independent section creation for ASM entry points
#include "CMT/ASM/PUSH.H"
SECTION (example_func) // example_func:
MOV32 (EAX, EBX) // mov rax, ebx
ADD32 (EAX, 1) // add eax, 1
RET // ret
END
#include "CMT/ASM/POP.H"
extern int example_func(void); // Connect it with CExample - Byte Copy Routine
#include "CMT/ASM/PUSH.H"
SECTION (copy_byte) // copy_byte:
MOV8 (MEM8(RDI), AL) // mov byte [rdi], al
RET // ret
END
#include "CMT/ASM/POP.H"Example - Add Value at Index
#include "CMT/ASM/PUSH.H"
SECTION (add_indexed) // add_indexed:
ADD32 (MEM32_INDEX(RAX, RCX), EDX) // add dword [rax + rcx], edx
RET // ret
END
#include "CMT/ASM/POP.H"Example - Setting a direct value to a register
#include "CMT/ASM/PUSH.H"
SECTION (return_42) // return_42:
MOV64 (RAX, VALUE(42)) // add rax, 42
RET // ret
END
#include "CMT/ASM/POP.H"
RAX - Read or Write the register RAX in Intel CPU (WIP)
[!WARNING]
THIS IS A WIP CONTENT!!! THIS EXTENSION MIGHT NOT WORK ON ALL COMPILERS, OPERATING SYSTEMS, OR ARCHITECTURES!!! MAJOR MAINTENANCE IS PLANNED! USE IT AT YOUR OWN RISK
β οΈ ImportantFile at: [π CMT/ASM/RAX.H]
#define INCL__RAX #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
GET_RAX, get_rax |
#define () |
Set RAX into a variable |
SET_RAX, set_rax |
#define () |
Set RAX |
With these functions, you're able to move and get values from the Intel CPU register RAX and stack with different data sizes and archs.
Using GET_RAX or SET_RAX on a non-Intel CPU will cause an error.
β οΈ NoteOn 32-BIT systems, EAX register is used.
Example 1: Read RAX into a variable
uint64_t myValue;
GET_RAX(myValue); // MOV RAX contents into myValue
// myValue now holds the value from RAXExample 2: Read EAX on 32-bit system
unsigned int myValue32;
GET_RAX(myValue32); // MOV EAX contents into myValue32
// myValue32 now holds the value from EAXExample 3: Setting RAX
uint64_t armValue = 42;
SET_RAX(armValue); // RAX is now 42
// or
SET_RAX(42); // RAX is now 42
X17 - Read or Write the register X17 in ARM CPU
β οΈ ImportantFile at: [π CMT/ASM/X17.H]
#define INCL__X17 #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
GET_X17, get_x17 |
#define () |
Set X17 into a variable |
SET_X17, set_x17 |
#define () |
Set X17 |
With these functions, you're able to move and get values from the ARM CPU register X17 and stack with different data sizes and archs.
Using GET_X17 or SET_X17 on a non-Intel CPU will cause an error.
β οΈ NoteOn 32-BIT systems, all functions used from this header will throw an error.
Example 1: Read X17 into a variable
uint64_t myValue;
GET_X17(myValue); // MOV X17 contents into myValue
// myValue now holds the value from X17Example 2: Setting X17
uint64_t myValue = 42;
SET_X17(myValue); // X17 now 42
// or
SET_X17(42); // X17 now 42
FAR - If a program compiles on a 16-bit system and you have a chunk of memory larger than 64 KB, you need this.
β οΈ ImportantFile at: [π CMT/ATTRIBUTES/FAR.H]
#define INCL__FAR #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
FAR |
#define |
Marks a memory segment or pointer as far, allowing access beyond 64KB limits. |
FAR defines the memory access type depending on the compiler and platform.
- On 16-bit systems, it expands to a far pointer keyword (e.g.,
far,_far). - On modern systems, it's often empty or unused.
Example - Variables: Creates an integer variable that uses a far memory pointer.
FAR int far_var;
far_var = 42;Example - Pointers: A string pointer stored in far memory space.
FAR char *far_ptr;
far_ptr = "Hello, world!";Example - Structs: A struct stored in far memory.
struct FAR far_struct
{
int id;
char name[20];
};Example - Function Pointers: A function pointer in far memory.
FAR void (*far_function)(void);Pro-Tip:
- Check your compiler documentation for memory models and pointer types.
- To support very old compilers, this file avoids using
#if. - On modern systems (32/64-bit), far pointers are obsolete and mostly ignored.
- Far pointers are slower (due to segment switching).
- Use
FARonly if you're planning to compile your code on 16-bit real mode environments.
FAR is a memory qualifier used to access memory locations beyond the current segment in 16-bit architectures.
In 16-bit compilers (MS-DOS, Turbo C, Watcom, etc.), pointers are:
- Near: within a single segment
- Far: across segments
How? Memory segmentation in 16-bit systems uses 64KB segments:
- Near pointer -> 16-bit offset
- Far pointer -> 32-bit (segment:offset)
Example:
int near_ptr_var; // accesses memory in the current segment.
int FAR far_ptr_var; // accesses memory across segments (full 1MB space).Function Pointers:
void (*near_func)(void); // can call functions within the same segment.
FAR void (*far_func)(void); // can call functions in different segments.
PACK - Packs and removes the padding bytes from your struct.
β οΈ ImportantFile at: [π CMT/ATTRIBUTES/PACK.H]
#define INCL__PACK #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
PRAGMA_PACK_PUSH |
#define |
Begins structure packing (works like #pragma pack(push)) |
PRAGMA_PACK_POP |
#define |
Ends structure packing (works like #pragma pack(pop)) |
PACK |
#define |
Applies packing to a specific struct |
Note: To fully use this feature, you must apply both
PACKandPRAGMA_PACK_*macros around the struct.
PRAGMA_PACK_PUSH
struct test
{
. . .
} PACK;
PRAGMA_PACK_POPor
PRAGMA_PACK_PUSH
typedef struct test
{
. . .
} PACK t_test;
PRAGMA_PACK_POPPACK removes padding bytes from your struct, reducing its size in memory.
However, avoid using this unless necessary (e.g., binary layouts, network packets), as removing padding may cause performance issues.
Why?
When the CPU reads a struct, it aligns memory access for performance. Typical alignments follow 1, 2, 4, 8, 16, etc. To match these, the compiler inserts padding bytes.
Example:
struct test
{
int a;
}; // sizeof = 4 bytesstruct test
{
int a;
char b;
}; // sizeof = 8 bytes (includes 3 bytes padding)Where did the 3 bytes go? -> Padding. Padding is added so the CPU can read memory efficiently.
Now packed:
PRAGMA_PACK_PUSH
struct test
{
int a;
char b;
} PACK;
PRAGMA_PACK_POPNow sizeof is 5 bytes, but access may be slower or unaligned on some systems. Sharing packed structs between functions or modules might lead to performance loss due to unaligned memory access.
REGPARM - Pass arguments to a function via using CPU registers. (WIP)
β οΈ ImportantFile at: [π CMT/ATTRIBUTES/REGPARM.H]
#define INCL__REGPARM #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
REGPARM, regparm |
#define () |
With this feature, you are able to send variables to functions using pure registers instead of using memory. |
Important - For more effective optimisation, use the register keyword on your arguments.
You must enter the number of variables passed to the function via registers in the argument: REGPARM(<number_of_registers>)
void REGPARM(1) function(int b)
{
. . .
}
void REGPARM(2) function(int a, int b)
{
. . .
}
void regparm(4) function(int a, float b, long c, char *d)
{
. . .
}For the most effective usage:
void regparm(2) function(
register int a,
register float b,
register char *c
)
{
. . .
}Using this keyword in your function lets you hold a value directly in a CPU register, allowing direct access-similar to C++ references but without using pointers.
When the program reaches this function, the CPU will use the existing value in a register instead of fetching it from memory (RAM) or pushing/popping values to/from the stack.
In other words, it avoids extra instructions during function calls to get the value.
Note: You must also use REGPARM in the function's prototype if one is declared:
void REGPARM(2) FUNCT(int A, int B) { ... } // Function definition
extern void REGPARM(2) FUNCT(int A, int B); // Function prototype
COMMA OPERATOR - Defines a macro if comma operator is supported on your compiler
β οΈ ImportantFile at: [π CMT/CHECK_FEATURE/COMMA_OPERATOR.H]
#define INCL__COMMA_OPERATOR #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
IS__COMMA_OPERATOR__SUPPORTED |
#define |
Defines if comma operator supported by your compiler |
Defines IS__COMMA_OPERATOR__SUPPORTED if the compiler supports the comma operator.
The comma operator refers to the (,) operator in macros or expressions, used to evaluate multiple expressions in sequence and return the last.
Example:
int a = (b++, funct(), c = 42, b += c, 66); // Performs all actions and returns 66
INLINE ASM - Defines a macro if inline asm is supported on your compiler and which type of syntax it is using
β οΈ ImportantFile at: [π CMT/CHECK_FEATURE/INLINE_ASM.H]
#define INCL__INLINE_ASM #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
IS__INLINE_ASM__SUPPORTED |
#define |
Defined if the compiler supports inline assembly in C/C++. |
INLINE_ASM_TYPE__GNU |
#define |
GNU-style extended inline assembly (GCC, Clang). |
INLINE_ASM_TYPE__MSVC |
#define |
Microsoft Visual C++ block-style assembly. |
INLINE_ASM_TYPE__BORLAND |
#define |
Borland/Turbo C simple inline assembly. |
INLINE_ASM_TYPE__AZTEC |
#define |
Aztec C block-style (x86). |
INLINE_ASM_TYPE__LATTICE |
#define |
Lattice C string-style (68K/Amiga). |
INLINE_ASM_TYPE__INTEL_MS |
#define |
Intel C MS-style single-line assembly. |
INLINE_ASM_TYPE__WATCOM |
#define |
Watcom C pragma/block-style assembly. |
INLINE_ASM_TYPE__ARM |
#define |
ARM compiler-style inline assembly with constraints. |
INLINE_ASM_TYPE__KEIL |
#define |
Keil embedded pragma-block-style assembly. |
INLINE_ASM_TYPE__HCCF |
#define |
HC(S) or ColdFire compiler-style inline assembly. |
INLINE_ASM_TYPE__ISO |
#define |
ISO-C extended inline assembly. |
This header detects whether the compiler supports inline assembly and, if so, identifies which syntax style it uses. Different compilers have different assembly embedding formats, and the library defines a macro for the matching type.
β οΈ ImportantPLEASE CHECK THE TABLE AT [
π CMT/INLINE_ASM_TABLE.md] FOR INFORMATION ABOUT SUPPORTED ARCHITECTURES.
int result; asm("movl %1, %0" : "=r"(result) : "r"(input_var) : );int input_var = 42; __asm { mov eax, input_var mov result, eax }asm mov eax, input_var asm mov result, eaxasm { mov ax, input_var mov result, ax }asm("move.l input_var,d0"); asm("move.l d0,result");__asm mov eax, input_var __asm mov result, eax_asm { mov eax, input_var mov result, eax } // or #pragma aux myhalt = "mov ax,4C00h" "int 21h"; void myhalt(void);__asm("mov %0,%1" : "=r"(result) : "r"(input_var));#pragma asm MOV A, #input_var MOV result, A #pragma endasmunsigned char result, input_var = 0X42; asm LDAA input_var asm STAA result // or #pragma asm LDAA input_var STAA result #pragma endasm__asm__ __volatile__ ( "movl %0, %%eax;" "movl %%eax, %1;" : "r"(input_var), "r"(result) : "=r"(result) // %0 : "r"(input_var) // %1 : "%eax" );
STRINGIFICATION - Defines a macro if stringification is supported on your compiler
β οΈ ImportantFile at: [π CMT/CHECK_FEATURE/STRINGIFICATION.H]
#define INCL__STRINGIFICATION #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
IS__STRINGIFICATION__SUPPORTED |
#define |
Defines if stringification supported by your compiler |
Defines IS__STRINGIFICATION__SUPPORTED if the compiler supports the stringification (#) feature.
Stringification refers to the # operator in macros, used to convert pure inputs to strings.
Example:
#define X(A) #A
printf( X( i am testing something ) ); // Will return: "i am testing something" without a new line.
TOKEN PASTNG - Defines a macro if token pasting is supported on your compiler
β οΈ ImportantFile at: [π CMT/CHECK_FEATURE/TOKEN_PASTING.H]
#define INCL__TOKEN_PASTING #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
IS__TOKEN_PASTING__SUPPORTED |
#define |
Defines if token pasting supported by your compiler |
Defines IS__TOKEN_PASTING__SUPPORTED if the compiler supports the token pasting (##) feature.
Token pasting refers to the ## operator in macros, used to merge two tokens into one during preprocessing.
Example:
#define AB(A, B) A##B
int AB(ma, in)(void) // Expands to: int main(void)
{
. . .
}
ARCHITECTURE - You can get information about your CPU architecture.
β οΈ ImportantFile at: [π CMT/ENVIRONMENTS/ARCHITECTURE.H]
#define INCL__ARCHITECTURE #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
__SYSTEM_64_BIT__ |
#define |
Defined if the system is 64-bit |
__SYSTEM_32_BIT__ |
#define |
Defined if the system is 32-bit |
__SYSTEM_31_BIT__ |
#define |
Defined if the system also 31-bit |
__SYSTEM_16_BIT__ |
#define |
Defined if the system is 16-bit |
__SYSTEM_8_BIT__ |
#define |
Defined if the system is 8-bit |
__SYSTEM_BIT__ |
#define |
Macro indicating the system bit-width |
| Name | Type | Description |
|---|---|---|
__HAS_128_BIT__ |
#define |
Defined if the system also supports 128-bit |
__HAS_64_BIT__ |
#define |
Defined if the system also supports 64-bit (for 32/16-bit systems) |
| Name | Type | Description |
|---|---|---|
__HAS_512_BIT_VECTOR__ |
#define |
Defined if the system supports 512-bit vector registers |
__HAS_256_BIT_VECTOR__ |
#define |
Defined if the system supports 256-bit vector registers |
__HAS_128_BIT_VECTOR__ |
#define |
Defined if the system supports 128-bit vector registers |
These defines indicate the bit-width supported or used by the system.
__SYSTEM__BIT__ is a macro that evaluates to the number of bits of the target architecture-typically 8, 16, 32, or 64-based on the CPU or compiler settings.
CPU - You can get information about CPU in runtime.
β οΈ ImportantFile at: [π CMT/ENVIRONMENTS/CPU.H]
#define INCL__CPU #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
__CPU_INTEL__ |
#define |
Defined if the target CPU is Intel (x86/x86-64) |
__CPU_ARM__ |
#define |
Defined if the target CPU is ARM-Based (aarch32/aarch64) |
__CPU_AMD_X86__ |
#define |
Defined if the target CPU is ARM-x86 |
__CPU_POWERPC__ |
#define |
Defined if the target CPU is IBM-PowerPC |
__CPU_RISCV__ |
#define |
Defined if the target CPU is RISC-V |
__CPU_HC12__ |
#define |
Defined if the target CPU is HC12 |
__CPU_HCS08__ |
#define |
Defined if the target CPU is HCS08 |
__CPU_COLDFIRE__ |
#define |
Defined if the target CPU is ColdFire |
__CPU_M68K__ |
#define |
Defined if the target CPU is M68K |
__CPU_SH__ |
#define |
Defined if the target CPU is SuperH |
__CPU_MIPS__ |
#define |
Defined if the target CPU is MIPS |
__CPU_SPARC__ |
#define |
Defined if the target CPU is SPARC |
__CPU_6502__ |
#define |
Defined if the target CPU is 6502 Microprocessor |
__CPU_Z80__ |
#define |
Defined if the target CPU is Z80 |
__CPU_TI__ |
#define |
Defined if the target CPU is TI-MCU Processor |
__CPU_SHARC__ |
#define |
Defined if the target CPU is SHARC |
__CPU_BLACKFIN__ |
#define |
Defined if the target CPU is Blackfin |
__CPU_DSP56K__ |
#define |
Defined if the target CPU is Motorola-DSP56000 |
__CPU_VER__ |
(*F)() |
Returns static C-string describing CPU vendor and version info at runtime. Same signature on both Intel and ARM builds |
This header provides CPU vendor/architecture detection and a runtime version query function with a unified signature across supported CPUs.
- At compile time, preprocessor checks detect the target CPU architecture (Intel x86/x86-64, AMD x86, ARM/AArch32/AArch64, PowerPC, RISC-V, etc.) and define the corresponding CPU_xxx macro.
- At runtime, CPU_VER() returns a static C-string containing the CPU vendor ID and a short model/version/stepping description.
- For x86-family CPUs, CPUID is used to obtain vendor and version fields.
- For ARM-family CPUs, MIDR_EL1 or MIDR is read and parsed.
- For other architectures, similar native registers or instructions can be implemented in the same interface.
Purpose: Allows any codebase to get a consistent CPU identification string without linking extra libraries, calling OS APIs, or using sprintf/printf.
KNR_STYLE - Creates a macro that determines whether the compiler uses the STD ANSI C standard or the K&R substandard 1989.
β οΈ ImportantFile at: [π CMT/ENVIRONMENTS/KNR_STYLE.H]
#define INCL__KNR_STYLE #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
KNR_STYLE |
#define |
Defined if compiler is using K&R style syntax. |
void |
#define |
Defined if in K&R. |
const |
#define |
Defined if in K&R. |
volatile |
#define |
Defined if in K&R. |
signed |
#define |
Defined if in K&R. |
unsigned |
#define |
Defined if in Microsoft C Compiler Version 1.0/2.0 |
Defines a macro if your compiler is using K&R style syntax or not.
CLEAR_INSTRUCTION_CACHE - Ensure freshly written machine code is executed
β οΈ ImportantFile at: [π CMT/FUNCTIONS/CLEAR_INSTRUCTION_CACHE.H]
#define INCL__CLEAR_INSTRUCTION_CACHE #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
CLEAR_INSTRUCTION_CACHE, clear_instruction_cache |
#define |
Ensures machine code written to memory is actually executed by the CPU |
Example:
memcpy(code_ptr, bytes, size);
CLEAR_INSTRUCTION_CACHE(code_ptr, code_ptr + size);
mprotect(page, RX);Modern CPUs often have separate data and instruction caches.
Writing bytes updates the data cache, but the instruction cache may still contain old contents for the same address.
Without a flush, the CPU may execute stale or garbage instructions.
After calling it:
- Data cache lines covering
[begin, end)are written back - Instruction cache lines covering
[begin, end)are invalidated - Next execution fetches the new instructions from memory
Architecture notes:
- x86: Usually no visible effect
- ARM / RISC-V / MIPS: Mandatory
DLL - Cross platform dynamic link library functions.
β οΈ ImportantFile at: [π CMT/FUNCTIONS/DLL.H]
#define INCL__DLL #include "CMT/CMT.H"
| NAME | TYPE | DESCRIPTION |
|---|---|---|
DLL, dll |
typedef |
A variable type for handling the opened DLL |
OPEN_DLL, open_dll |
#define() |
Open a DLL file |
READ_DLL, read_dll |
#define() |
Call a function pointer from the DLL file |
CLOSE_DLL, close_dll |
#define() |
Close DLL file |
DYNAMIC, dynamic |
#define |
Set function/variable as dynamically linkable |
To create dynamic link libraries, first compile your .c file with the -c flag (to produce object file), then link it with -shared:
Example:
my_dll.c
#include <stdio.h>
void dynamic my_write(void)
{
printf("TEST\n");
}Compile with:
gcc -c my_dll.c -o my_sll.o
gcc -shared my_sll.o -o my_dll.dllCongrats! You now created a dynamic link library!
Example:
{
dll dll_file;
dll_file = open_dll("my_dll.dll");
if (!dll_file) // DLL file couldn't open or not a DLL
exit(1);
void (*my_write)(); // Function pointer
my_write = (void (*)()) read_dll(dll_file, "my_write");
if (!my_write) // Function doesn't exist in DLL
exit(1);
my_write();
close_dll(dll_file);
// DO NOT close the DLL before using a function from it!
// It will cause a segmentation error!
}Dynamic Link Libraries are separate files from executables that need functions/variables from them.
You can get a function pointer from a DLL file and use it directly.
But this library just handles cross-platform keywords for that.
Why compile the whole printf unction into every .exe file? Just strue printf in a DLL file and connect all EXEs to it.
Your ~40KB .exe files will become ~100 bytes. Crazy, right?
- For OS/2 16-BIT or AmigaOS-3, use your function's ordinal number to retrieve the function pointer from the dll.
- Refer to your compiler's documentation for details on compiling dlls.
PREFETCH - Reduces the cache-miss latency of memory accesses. (WIP)
β οΈ ImportantFile at: [π CMT/FUNCTIONS/PREFETCH.H]
#define INCL__PREFETCH #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
PREFETCH, prefetch |
#define () |
Make a value from memory preloaded into CPU cache before use |
PREFETCHW, prefetchw |
#define () |
Same as PREFETCH, but tells CPU the data may be modified |
SPIN_LOCK_PREFETCH, spin_lock_prefetch |
#define () |
Prefetch spin locks (only on UNIX) |
PREFETCH_RANGE, prefetch_range |
(*f)() |
Prefetch the whole memory block |
Example:
int variable[9471];
int number = 42;
PREFETCH(variable[0]);
// OR
prefetch(number);Example:
int variable[9471];
PREFETCH_RANGE(variable, sizeof(variable));
// OR
prefetch_range(variable, 100);You're telling the CPU to load a specific memory location into its cache before it's actually needed.
Simply put: it hints the CPU to fetch the value into cache early, reducing memory access latency when that value is used shortly after.
Note: This does not move data to the stack - it moves it into cache lines. It's a performance hint, not a guaranteed action.
The PREFETCH function only works for a single variable.
PREFETCH_RANGE allows you to prefetch multiple variables or a whole array by prefetching a block of memory in a loop or range.
Use this when working with large buffers or arrays to minimize cache misses before heavy processing.
READ_FILE - Reads a file and returns it's content to you. (WIP)
β οΈ ImportantFile at: [π CMT/FUNCTIONS/READ_FILE.H]
#define INCL__READ_FILE #include "CMT/CMT.H"
| NAME | TYPE | DESCRIPTION |
|---|---|---|
READ_FILE, read_file |
(*f)() |
Reads a file and returns its content |
S_FILE, s_file |
struct |
Struct type returned by READ_FILE |
T_FILE, t_file |
typedef |
typedef struct s_file, struct s_file |
| Code | Description |
|---|---|
| 0 | File opened and read successfully |
| 1 | File does not exist |
| 2 | Failed to allocate necessary memory from RAM for struct's data |
| 3 | Permission denied while trying to read the file |
struct s_file
{
size_t size; // Size of the file (bytes)
char *data; // Every byte of the file
};Reads the entire contents of a file specified by file_path into memory and stores it in the provided s_file structure.
Yes, you must free the "data" field of the structure when you're done.
THREAD - Use and manage multithreading in your application across platforms.
β οΈ ImportantFile at: [π CMT/FUNCTIONS/THREAD.H]
#define INCL__THREAD #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
THREAD_CREATE, thread_create |
(*F)() | Create a thread by providing a function |
THREAD_JOIN, thread_join |
(*F)() | Wait for a thread to finish executing |
MUTEX_CREATE, mutex_create |
(*F)() | Create a mutex |
MUTEX_DESTROY, mutex_destroy |
(*F)() | Destroy a mutex |
MUTEX_LOCK, mutex_lock |
#define() | Lock a mutex |
MUTEX_UNLOCK, mutex_unlock |
#define() | Unlock a mutex |
T_THREAD, t_thread |
typedef | Thread ID type |
T_MUTEX, t_mutex |
typedef | Mutex ID type |
Creates a new thread. Takes a function pointer and an argument for that function. Returns a thread handle (t_thread), or NULL on failure.
Example:
void *my_thread(void *arg)
{
// ...
return 0;
}
{
t_thread thread = thread_create(my_thread, NULL);
if (!thread)
{
// handle error
}
}Waits for a thread to finish and frees its handle. Takes the thread handle and a void** for the return value (can be NULL). Returns 0 on success.
Example:
void *ret;
thread_join(thread, &ret);Allocates and initializes a mutex. Returns a mutex handle (t_mutex). Returns NULL on failure.
Example:
t_mutex mutex = mutex_create();
if (!mutex)
{
// handle error
}Destroys and frees a mutex created with mutex_create. Returns 0 on success.
Example:
mutex_destroy(mutex);Locks a mutex.
Example:
mutex_lock(mutex);Unlocks a mutex.
Example:
mutex_unlock(mutex);For detailed information and examples, please refer to the POSIX Threads (pthreads) documentation.
- Always join threads you create to avoid resource leaks.
- Always destroy mutexes after use.
- All wrappers return
NULLor1on failure, not-1. - Handles (
t_thread,t_mutex) are pointers and must not be copied.
- For POSIX versions earlier than 1003.1c, define
__TRY__LINUX_THREADSto enable these functions. - For Plan9 OS, define
__TRY__PLAN9. - For FreeRTOS, define
__TRY__FREE_RTOS. - For Zephyr OS, define
__TRY__ZEPHYR. - For TI-RTOS, define
__TRY__TIRTOS. - See the supported platforms table below for details.
| PLATFORM | NOTES |
|---|---|
| UNIX | POSIX -1003.1C (<=1995): Also known as LinuxThreads - Robust or recursive mutexes not fully reliable - Signals + threads are buggy - TLS and priority inheritance unreliable - Avoid using threads inside fork()POSIX +1003.1C (>1995): All functions fully supported |
| WINDOWS/WINAPI | All functions fully supported |
| OS/2 - 32-bit | All functions fully supported |
| OS/2 - 16-bit | - THREAD_CREATE: Cannot send argument to thread function- THREAD_JOIN: Not supported, always returns -1 |
| HAIKU | All functions fully supported |
| PLAN9 | All functions fully supported |
| FREERTOS | THREAD_JOIN not supported, always returns -1 |
| ZEPHYR | THREAD_JOIN cannot get return value of thread function |
| VXWORKS | THREAD_JOIN not supported, always returns -1 |
| TI-RTOS | THREAD_JOIN not supported, always returns -1 |
If your OS is not listed, these features may not be supported on your device, OS, or compiler.
IGNORE_VAR - Tag the variables that may not be used in the project. (For ignoring warnings)
β οΈ ImportantFile at: [π CMT/KEYWORDS/IGNORE_VAR.H]
#define INCL__IGNORE_VAR #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
IGNORE_VAR, ignore_var |
#define |
Tells the compiler the variable may not be used |
To use, simply add the tag in front of the variable.
IGNORE_VAR void *test;
ignore_var int i;int main(int argc, char **argv)
{
ignore_var argc;
...
}This keyword tells the compiler that the variable may not be used in the program.
If unused, the compiler ignores this variable and continues compiling the program without generating any warnings.
INLINE - Inline your function on specific compilers.
β οΈ ImportantFile at: [π CMT/KEYWORDS/INLINE.H]
#define INCL__INLINE #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
INLINE |
#define |
The function is now inlined, similar to a #include. |
Just put this tag at the beginning of the function.
Example:
INLINE void function(void)
{
. . .
}- Avoid using
static inlineif possible.
Using static with INLINE cancels the effect or limits visibility.
If the function is only called once or twice, it might not get inlined.
If unused, it may still take up a symbol. Not worth the risk.
Bad Example:
// This will make your function just a static:
static INLINE int test(void)
{
return (15 + 42);
}- Don't use
goto
Technically allowed, but may break optimisation badly.
Bad Example:
// This is wrong... This feels so wrong:
INLINE int test(void)
{
LAYER:
. . .
goto LAYER;
}- No
staticvariables inside inline functions
Bad Example:
// what da dog doing?
INLINE void test(void)
{
static int dog;
. . .
}- No recursion
Inline functions can't call themselves.
Bad Example:
// You can't call something that doesn't exist in the binary.
INLINE void inline_test(int a)
{
inline_test(a + 1);
. . .
}- No
va_list,va_arg, or...
Bad Example:
// No, don't! PLEASE! I BEG YOU!!!
INLINE void inline_test(int a, ...)
{
va_list list;
. . .
}It replaces the call to the inline function with its actual code at the call site.
Example:
// Inline function:
INLINE int test(void)
{
return (15 + 42);
}
// Used in main:
int main(void)
{
printf("%d", test());
return (0);
}
// Becomes this during compilation:
int main(void)
{
printf("%d", (15 + 42));
return (0);
}This was just an example. Youβre free to write whatever you want inside the function - but be aware of what not to do by reading the "How Not to Use It" section above.
[[clang::always_inline]]doesn't work reliably in Clang. Believe me.- Use this keyword with your
staticfunctions and those defined in header files. - You don't need to map inline functions with MVS linker pragmas (like
#pragma map) when targeting z/OS systems. - Inline functions donβt generate external symbols unless inlining fails.
LOCAL - Ensure each thread has its own independent copy of a "global" or "static" variable it used on.
β οΈ ImportantFile at: [π CMT/KEYWORDS/LOCAL.H]
#define INCL__LOCAL #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
LOCAL, local |
#define |
Make a variable thread-local (separate per thread). |
For Global Variables:
local int g_variable = 0;For Static Variables:
void test(void)
{
static local int variable = 0;
}This keyword must only be used with global or static variables when needed.
It marks the variable as thread-local, meaning each thread gets its own separate instance of that variable.
This ensures the variable is not shared between threads, preventing race conditions or unintended data sharing.
Yes, you must use the local keyword in the prototypes of global variables too.
extern local int g_variable;
// ^^^^^Add that if you're trying to access your global variable using the extern keyword.
NORETURN - Optimise your function if there is a direct exit in it.
β οΈ ImportantFile at: [π CMT/KEYWORDS/NORETURN.H]
#define INCL__NORETURN #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
NORETURN, noreturn |
#define |
Tells the compiler that the program will end in this function. |
Just put this tag at the beginning of the function.
Example:
NORETURN void function(void)
{
. . .
if (...)
exit(0);
. . .
}
noreturn void function(void)
{
. . .
if (...)
exit(0);
. . .
}Warns the compiler that the function may terminate the program without returning to main().
The only way to do that is by using the exit() function.
This is used for optimisation purposes.
ROM - Place constants in read-only / program memory when possible
β οΈ ImportantFile at: [π CMT/KEYWORDS/ROM.H]
#define INCL__ROM #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
ROM |
#define |
Places variables into read-only memory (ROM / flash) using the best mechanism for the target compiler and architecture |
For constants and lookup tables:
ROM int table[] = { 1, 2, 3, 4 };For strings:
ROM char message[] = "Hello";ROM abstracts read-only memory placement across compilers and architectures.
Depending on the target, it expands to:
constexprorconst(C / C++)PROGMEMon AVR- Compiler-specific section attributes
- Flash-specific qualifiers on embedded toolchains
The goal is to ensure data is stored in non-writable memory, conserving RAM and enforcing immutability.
ROMis intended for data objects, not functions- Do not assume runtime access semantics are identical on all platforms
- On AVR, special access macros may be required for
PROGMEMdata
STDCALL - A calling convention (mainly on Windows) where the callee cleans the stack and parameters are passed right-to-left
β οΈ ImportantFile at: [π CMT/KEYWORDS/STDCALL.H]
#define INCL__STDCALL #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
STDCALL |
#define |
Optimise the function to OS ABI rules. |
int STDCALL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, ...)
{
. . .
}Defines a calling convention where arguments are passed on the stack, the callee cleans the stack, and (on 32-bit systems) the function name may be decorated.
Primarily associated with Windows APIs but supported by multiple compilers for binary-interface compatibility.
Yes, you must use the STDCALL keyword in your function prototypes:
extern STDCALL int WinMain(...);
// ^^^^^^^
TRY_CATCH - Use original "try", "catch()", and "throw()" keywords in C Language like in JavaScript.
β οΈ ImportantFile at: [π CMT/KEYWORDS/TRY_CATCH.H]
#define INCL__TRY_CATCH #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
TRY, try |
#define |
Begins a block that may throw an error |
CATCH, catch |
#define() |
Catches thrown errors from the try block |
THROW, throw |
#define() |
Immediately jumps to catch with a specified error |
The setup section is optional if you are compiling for ARM 32-bit and never include this addon in a C file that has a
main()function.Otherwise, skip setup and jump to Contents at the bottom. You don't have to do Setup process.
If a source file does not contain main(), and you still use the library, then one source file must define CMT_SETUP.
This ensures that any global variables or link-exposed functions are properly defined.
After doing this once, you can include this header anywhere else without defining the macro again; other files will only see the extern declarations.
Example:
#define CMT_SETUP
#include "CMT/KEYWORDS/TRY_CATCH.H"Or
#define CMT_SETUP
#define INCL__TRY_CATCH
#include "CMT/CMT.H"-
try{...} - Wraps a block of code that may callthrow(...)within it. -
catch(int){...} - Placed right after try. Captures the error code passed fromthrow(...). -
throw(int) - Throws an error code and jumps to the nearestcatch()block.
Basic Try/Catch:
try
{
if (1)
throw (99);
}
catch (int err)
{
printf("ERROR: %d\n", err);
}Compilers like TCC do not allow variable declarations inside control clauses (e.g. for (int a;...)).
So, declare int err; outside the catch() scope:
{
int err;
try
{
if (1)
throw (99);
}
catch (err)
{
printf("ERROR: %d\n", err);
}
}#include <stdio.h>
#include "CMT/KEYWORDS/TRY_CATCH.H"
void test(void)
{
throw (42);
}
int main(void)
{
try
{
test();
}
catch (int err)
{
printf("ERROR: %d\n", err);
}
return (0);
}{
try
{
try {
throw (42);
}
catch (int error) {
printf("err_1: %d\n", error);
throw (32);
}
}
catch (int error)
{
printf("err_2: %d\n", error);
}
}This custom error handling system uses setjmp and longjmp to mimic a basic form of exception handling in C.
trysets a jump point for where the program should return if an error occurs.throw(err)stores the error value and jumps back to that point.catch(variable)allows you to access the thrown error code.
- Only supports
throw()andcatch()withinttype. - If compiled with a C++ compiler, the library will not define the
try,catch, andthrowmacros but definesTRY,CATCH, andTHROWanyway. - Avoid using this system on constrained devices like 8051, PIC, MSP430, etc. It may quickly bloat memory. Use it only on devices with more than 4KB of RAM.
- Do not exit from
tryusingreturnorgoto, this may cause segmentation faults or unexpected results. Instead, use:
throw (0); // to completely exit the try block
TYPES - Standard and fixed-size types with support macros for portability
β οΈ ImportantFile at: [π CMT/KEYWORDS/TYPES.H]
#define INCL__TYPES #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
BYTE, byte |
typedef | Unsigned one byte storage unit (char) |
LET, let |
typedef | Type for array indexing and sizes (like size_t) |
VAR, var |
typedef | Type capable of holding maximum addressable value |
PTR, ptr |
typedef | Generic pointer type |
| Name | Type | Description |
|---|---|---|
BIT8, bit8 |
typedef | 8-bit unsigned or signed integer |
BIT16, bit16 |
typedef | 16-bit unsigned or signed integer |
BIT32, bit32 |
typedef | 32-bit unsigned or signed integer |
BIT64, bit64 |
typedef | 64-bit unsigned or signed integer |
BIT128, bit128 |
typedef | 128-bit unsigned or signed integer (if supported) |
| Name | Type | Description |
|---|---|---|
FLOAT32, float32 |
typedef | 32-bit single-precision floating-point |
FLOAT64, float64 |
typedef | 64-bit double-precision floating-point |
FLOAT128, float128 |
typedef | 128-bit extended-precision floating-point |
| Name | Type | Description |
|---|---|---|
SUPPORTED__BIT64 |
#define | Defined if compiler supports BIT64 type |
SUPPORTED__BIT128 |
#define | Defined if compiler supports BIT128 type |
SUPPORTED__FLOAT64 |
#define | Defined if compiler supports FLOAT64 type |
SUPPORTED__FLOAT128 |
#define | Defined if compiler supports FLOAT128 type |
Note:
Some types may not be defined if the platform or compiler does not support them.
For example,FLOAT64may not be available if the nativedoubletype is only 32 bits on your system.
Similarly,FLOAT128andBIT128are only defined if the compiler and architecture support 128-bit values.Users should check the corresponding macros (e.g.,
SUPPORTED__FLOAT64,SUPPORTED__FLOAT128,SUPPORTED__BIT128) before using these types to ensure portability and correctness.
UNUSED - Tag the functions that may not used in the project. (For ignore warnings)
β οΈ ImportantFile at: [π CMT/KEYWORDS/UNUSED.H]
#define INCL__UNUSED #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
UNUSED, unused |
#define |
Tells the compiler that the function may not be used |
Just put this tag at the beginning of the function.
Example:
UNUSED void function(void)
{
. . .
}
unused void function(void)
{
. . .
}This keyword tells the compiler that the function may not be used in the program.
If unused, the compiler ignores this function and continues compiling the program without giving any warnings.
VA_ARGS - Make "va_args" system work on older compilers (For before C89)
β οΈ ImportantFile at: [π CMT/KEYWORDS/VA_ARGS.H]
#define INCL__VA_ARGS #include "CMT/CMT.H"
| Name | Type | Description |
|---|---|---|
va_list, VA_LIST |
typedef |
A type for creating your argument list |
va_arg, VA_ARG |
#define() |
Get a variable from your va_list in your function |
va_copy, VA_COPY |
#define() |
Copy your va_list address |
va_start, VA_START |
#define() |
Get the address pointer of your argument list |
va_end, VA_END |
#define() |
End a va_list pointer |
test(); // PROTOTYPE
int main()
{
test(42, 42.0, "Hello world");
return (0);
}
extern int printf(); // PROTOTYPE
test(start)
int start;
{
va_list x;
va_list y;
va_start(x, start);
va_copy(y, x);
printf("%d\nx:%f\n", start, va_arg(x, double));
va_end(x);
printf("y:%f\n", va_arg(y, double));
printf("y:%s\n", va_arg(y, char *));
va_end(y);
}This library allows use of va_args-style variable argument functions on pre-C89 compilers.
- Β© These icons are inspired by the style of Windows 2000 icons. They are original creations and are not affiliated with or endorsed by Microsoft.






