mirror of
https://github.com/nicbarker/clay.git
synced 2025-12-24 01:51:05 +00:00
Comprehensive tutorials for advanced C topics: - 15_preprocessor_macros.md (800+ lines) * Complete preprocessor guide * #include, #define, conditional compilation * Header guards, function-like macros * Multi-line macros, stringification, token pasting * Predefined macros, best practices * Extensive Clay examples showing macro patterns - 16_advanced_macros.md (900+ lines) * Variadic macros with __VA_ARGS__ * X-Macros pattern for code generation * _Generic for type-based selection (C11) * Compound literals and statement expressions * For-loop macro trick (Clay's CLAY() macro explained) * Designated initializers in macros * Recursive macro techniques * Complete breakdown of Clay's macro system - 17_memory_management.md (850+ lines) * Stack vs heap memory comparison * malloc, calloc, realloc, free usage * Common memory errors and prevention * Memory leak detection with Valgrind * Arena allocators (Clay's approach) * Memory pools for performance * Memory alignment optimization * Custom allocators * Clay's zero-allocation strategy * Best practices and profiling All files include: - 50+ code examples per chapter - Real Clay library usage throughout - Practice exercises - Performance considerations - Professional patterns Total new content: ~2,500 lines of detailed tutorials
923 lines
17 KiB
Markdown
923 lines
17 KiB
Markdown
# Chapter 15: Preprocessor and Macros in C
|
|
|
|
## Complete Guide with Clay Library Examples
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
1. What is the Preprocessor?
|
|
2. #include Directive
|
|
3. #define - Simple Macros
|
|
4. Conditional Compilation
|
|
5. Header Guards
|
|
6. Function-like Macros
|
|
7. Multi-line Macros
|
|
8. Stringification (#)
|
|
9. Token Pasting (##)
|
|
10. Predefined Macros
|
|
11. Macro Best Practices
|
|
12. Clay's Macro Usage
|
|
|
|
---
|
|
|
|
## 15.1 What is the Preprocessor?
|
|
|
|
The preprocessor runs **before** compilation and performs text substitution.
|
|
|
|
```c
|
|
// Source file (.c)
|
|
#define MAX 100
|
|
|
|
int main(void) {
|
|
int arr[MAX]; // Preprocessor replaces MAX with 100
|
|
return 0;
|
|
}
|
|
|
|
// After preprocessing
|
|
int main(void) {
|
|
int arr[100];
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Preprocessing steps:**
|
|
1. Remove comments
|
|
2. Process #include directives
|
|
3. Expand macros
|
|
4. Handle conditional compilation
|
|
5. Output preprocessed code to compiler
|
|
|
|
**View preprocessed output:**
|
|
```bash
|
|
gcc -E program.c # Output to stdout
|
|
gcc -E program.c -o program.i # Save to file
|
|
```
|
|
|
|
---
|
|
|
|
## 15.2 #include Directive
|
|
|
|
Include header files in your code.
|
|
|
|
### Standard Library Headers
|
|
|
|
```c
|
|
#include <stdio.h> // System header (angle brackets)
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
```
|
|
|
|
**Search path:** System include directories (/usr/include, etc.)
|
|
|
|
### User Headers
|
|
|
|
```c
|
|
#include "myheader.h" // User header (quotes)
|
|
#include "utils/math.h"
|
|
```
|
|
|
|
**Search path:**
|
|
1. Current directory
|
|
2. Directories specified with -I
|
|
3. System directories
|
|
|
|
### How #include Works
|
|
|
|
```c
|
|
// Before preprocessing
|
|
#include "myheader.h"
|
|
|
|
int main(void) {
|
|
// code
|
|
}
|
|
|
|
// After preprocessing (myheader.h contents inserted)
|
|
// Contents of myheader.h
|
|
void myFunction(void);
|
|
typedef struct { int x, y; } Point;
|
|
|
|
int main(void) {
|
|
// code
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// Clay is a single-header library
|
|
#define CLAY_IMPLEMENTATION
|
|
#include "clay.h"
|
|
|
|
// No other includes needed - zero dependencies!
|
|
```
|
|
|
|
---
|
|
|
|
## 15.3 #define - Simple Macros
|
|
|
|
Define constants and simple text replacements.
|
|
|
|
### Constant Macros
|
|
|
|
```c
|
|
#define PI 3.14159
|
|
#define MAX_SIZE 1000
|
|
#define BUFFER_SIZE 256
|
|
#define VERSION "1.0.0"
|
|
|
|
int main(void) {
|
|
float radius = 5.0f;
|
|
float area = PI * radius * radius; // PI replaced with 3.14159
|
|
|
|
char buffer[BUFFER_SIZE];
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h:102):
|
|
```c
|
|
#define CLAY_VERSION_MAJOR 0
|
|
#define CLAY_VERSION_MINOR 12
|
|
#define CLAY_VERSION_PATCH 0
|
|
|
|
// Used to check version at compile time
|
|
#if CLAY_VERSION_MAJOR >= 1
|
|
// Version 1.0 or higher
|
|
#endif
|
|
```
|
|
|
|
### Expression Macros
|
|
|
|
```c
|
|
#define SQUARE(x) ((x) * (x))
|
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
#define IS_EVEN(n) (((n) & 1) == 0)
|
|
|
|
int main(void) {
|
|
int sq = SQUARE(5); // 25
|
|
int max = MAX(10, 20); // 20
|
|
int even = IS_EVEN(4); // 1 (true)
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Important:** Always use parentheses!
|
|
|
|
```c
|
|
// WRONG
|
|
#define SQUARE(x) x * x
|
|
int result = SQUARE(2 + 3); // Expands to: 2 + 3 * 2 + 3 = 11 (wrong!)
|
|
|
|
// RIGHT
|
|
#define SQUARE(x) ((x) * (x))
|
|
int result = SQUARE(2 + 3); // Expands to: ((2 + 3) * (2 + 3)) = 25 (correct!)
|
|
```
|
|
|
|
**Clay Example** (from clay.h:111):
|
|
```c
|
|
#define CLAY__MAX(x, y) (((x) > (y)) ? (x) : (y))
|
|
#define CLAY__MIN(x, y) (((x) < (y)) ? (x) : (y))
|
|
|
|
// Usage in Clay's layout calculations
|
|
float width = CLAY__MAX(minWidth, calculatedWidth);
|
|
```
|
|
|
|
---
|
|
|
|
## 15.4 Conditional Compilation
|
|
|
|
Compile different code based on conditions.
|
|
|
|
### #ifdef and #ifndef
|
|
|
|
```c
|
|
#define DEBUG
|
|
|
|
int main(void) {
|
|
#ifdef DEBUG
|
|
printf("Debug mode enabled\n");
|
|
#endif
|
|
|
|
#ifndef RELEASE
|
|
printf("Not in release mode\n");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### #if, #elif, #else, #endif
|
|
|
|
```c
|
|
#define VERSION 2
|
|
|
|
#if VERSION == 1
|
|
printf("Version 1\n");
|
|
#elif VERSION == 2
|
|
printf("Version 2\n");
|
|
#else
|
|
printf("Unknown version\n");
|
|
#endif
|
|
```
|
|
|
|
### defined() Operator
|
|
|
|
```c
|
|
#if defined(DEBUG) && defined(VERBOSE)
|
|
printf("Debug and verbose mode\n");
|
|
#endif
|
|
|
|
// Equivalent to
|
|
#if defined(DEBUG)
|
|
#if defined(VERBOSE)
|
|
printf("Debug and verbose mode\n");
|
|
#endif
|
|
#endif
|
|
```
|
|
|
|
**Clay Example** (from clay.h:88):
|
|
```c
|
|
// Platform-specific exports
|
|
#if defined(_MSC_VER)
|
|
#define CLAY_WASM __declspec(dllexport)
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
#define CLAY_WASM __attribute__((visibility("default")))
|
|
#else
|
|
#define CLAY_WASM
|
|
#endif
|
|
```
|
|
|
|
### Platform Detection
|
|
|
|
```c
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#define SLEEP(ms) Sleep(ms)
|
|
#elif defined(__linux__)
|
|
#include <unistd.h>
|
|
#define SLEEP(ms) usleep((ms) * 1000)
|
|
#elif defined(__APPLE__)
|
|
#include <unistd.h>
|
|
#define SLEEP(ms) usleep((ms) * 1000)
|
|
#endif
|
|
|
|
int main(void) {
|
|
SLEEP(1000); // Sleep 1 second on any platform
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// SIMD optimization based on platform
|
|
#if !defined(CLAY_DISABLE_SIMD)
|
|
#if defined(__x86_64__) || defined(_M_X64)
|
|
#include <emmintrin.h>
|
|
// Use SSE2 instructions
|
|
#elif defined(__aarch64__)
|
|
#include <arm_neon.h>
|
|
// Use NEON instructions
|
|
#endif
|
|
#endif
|
|
```
|
|
|
|
---
|
|
|
|
## 15.5 Header Guards
|
|
|
|
Prevent multiple inclusion of same header.
|
|
|
|
### Traditional Header Guards
|
|
|
|
```c
|
|
// myheader.h
|
|
#ifndef MYHEADER_H
|
|
#define MYHEADER_H
|
|
|
|
// Header contents
|
|
void myFunction(void);
|
|
|
|
typedef struct {
|
|
int x, y;
|
|
} Point;
|
|
|
|
#endif // MYHEADER_H
|
|
```
|
|
|
|
**How it works:**
|
|
1. First inclusion: `MYHEADER_H` not defined, content is included
|
|
2. Second inclusion: `MYHEADER_H` already defined, content skipped
|
|
|
|
### #pragma once (Non-standard but widely supported)
|
|
|
|
```c
|
|
// myheader.h
|
|
#pragma once
|
|
|
|
// Header contents
|
|
void myFunction(void);
|
|
```
|
|
|
|
**Clay Example** (from clay.h:82):
|
|
```c
|
|
#ifndef CLAY_HEADER
|
|
#define CLAY_HEADER
|
|
|
|
// All Clay declarations (4000+ lines)
|
|
|
|
typedef struct {
|
|
float x, y;
|
|
} Clay_Vector2;
|
|
|
|
// ... more declarations
|
|
|
|
#endif // CLAY_HEADER
|
|
```
|
|
|
|
---
|
|
|
|
## 15.6 Function-like Macros
|
|
|
|
Macros that look like functions.
|
|
|
|
### Basic Function-like Macros
|
|
|
|
```c
|
|
#define ABS(x) ((x) < 0 ? -(x) : (x))
|
|
#define SQUARE(x) ((x) * (x))
|
|
#define SWAP(a, b, type) { type temp = a; a = b; b = temp; }
|
|
|
|
int main(void) {
|
|
int abs_val = ABS(-5); // 5
|
|
int squared = SQUARE(4); // 16
|
|
|
|
int x = 10, y = 20;
|
|
SWAP(x, y, int);
|
|
printf("%d %d\n", x, y); // 20 10
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### Macros vs Functions
|
|
|
|
**Advantages of macros:**
|
|
- ✅ No function call overhead
|
|
- ✅ Type-generic (works with any type)
|
|
- ✅ Can access local variables
|
|
|
|
**Disadvantages:**
|
|
- ❌ Code bloat (expanded everywhere)
|
|
- ❌ No type checking
|
|
- ❌ Side effects with multiple evaluation
|
|
- ❌ Harder to debug
|
|
|
|
### Multiple Evaluation Problem
|
|
|
|
```c
|
|
#define SQUARE(x) ((x) * (x))
|
|
|
|
int main(void) {
|
|
int n = 5;
|
|
int result = SQUARE(n++);
|
|
// Expands to: ((n++) * (n++))
|
|
// n is incremented TWICE!
|
|
printf("n = %d, result = %d\n", n, result); // n = 7, result = 30
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Solution:** Use inline functions when possible:
|
|
|
|
```c
|
|
static inline int square(int x) {
|
|
return x * x;
|
|
}
|
|
|
|
int main(void) {
|
|
int n = 5;
|
|
int result = square(n++); // Only increments once
|
|
printf("n = %d, result = %d\n", n, result); // n = 6, result = 25
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// Clay uses inline functions when type safety matters
|
|
static inline float Clay__Min(float a, float b) {
|
|
return a < b ? a : b;
|
|
}
|
|
|
|
static inline float Clay__Max(float a, float b) {
|
|
return a > b ? a : b;
|
|
}
|
|
|
|
// But uses macros for compile-time constants
|
|
#define CLAY_PADDING_ALL(amount) \
|
|
(Clay_Padding){amount, amount, amount, amount}
|
|
```
|
|
|
|
---
|
|
|
|
## 15.7 Multi-line Macros
|
|
|
|
Use backslash to continue lines.
|
|
|
|
### Basic Multi-line Macro
|
|
|
|
```c
|
|
#define PRINT_COORDS(x, y) \
|
|
printf("X: %d\n", x); \
|
|
printf("Y: %d\n", y);
|
|
|
|
int main(void) {
|
|
PRINT_COORDS(10, 20);
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### do-while(0) Trick
|
|
|
|
Makes macros behave like statements:
|
|
|
|
```c
|
|
// WRONG - breaks in if statements
|
|
#define SWAP(a, b, type) \
|
|
type temp = a; \
|
|
a = b; \
|
|
b = temp;
|
|
|
|
if (condition)
|
|
SWAP(x, y, int); // Syntax error!
|
|
else
|
|
other();
|
|
|
|
// RIGHT - works everywhere
|
|
#define SWAP(a, b, type) \
|
|
do { \
|
|
type temp = a; \
|
|
a = b; \
|
|
b = temp; \
|
|
} while(0)
|
|
|
|
if (condition)
|
|
SWAP(x, y, int); // Works correctly!
|
|
else
|
|
other();
|
|
```
|
|
|
|
**Why it works:**
|
|
- `do { } while(0)` is a single statement
|
|
- Can be used anywhere a statement is expected
|
|
- while(0) ensures it runs exactly once
|
|
- Semicolon at end required by user
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
#define CLAY__ASSERT(condition) \
|
|
do { \
|
|
if (!(condition)) { \
|
|
Clay__currentContext->errorHandler.errorHandlerFunction( \
|
|
CLAY__INIT(Clay_ErrorData) { \
|
|
.errorType = CLAY_ERROR_TYPE_ASSERTION_FAILED, \
|
|
.errorText = CLAY_STRING("Assertion failed: " #condition), \
|
|
.userData = Clay__currentContext->errorHandler.userData \
|
|
} \
|
|
); \
|
|
} \
|
|
} while(0)
|
|
|
|
// Usage
|
|
CLAY__ASSERT(element != NULL); // Works correctly
|
|
```
|
|
|
|
---
|
|
|
|
## 15.8 Stringification (#)
|
|
|
|
Convert macro argument to string.
|
|
|
|
### Basic Stringification
|
|
|
|
```c
|
|
#define TO_STRING(x) #x
|
|
|
|
int main(void) {
|
|
printf("%s\n", TO_STRING(Hello)); // "Hello"
|
|
printf("%s\n", TO_STRING(123)); // "123"
|
|
printf("%s\n", TO_STRING(int x = 10)); // "int x = 10"
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### Practical Example
|
|
|
|
```c
|
|
#define PRINT_VAR(var) printf(#var " = %d\n", var)
|
|
|
|
int main(void) {
|
|
int age = 25;
|
|
int count = 100;
|
|
|
|
PRINT_VAR(age); // Prints: age = 25
|
|
PRINT_VAR(count); // Prints: count = 100
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### Double Stringification
|
|
|
|
```c
|
|
#define STRINGIFY(x) #x
|
|
#define TO_STRING(x) STRINGIFY(x)
|
|
|
|
#define VERSION_MAJOR 1
|
|
#define VERSION_MINOR 2
|
|
#define VERSION_PATCH 3
|
|
|
|
#define VERSION_STRING \
|
|
TO_STRING(VERSION_MAJOR) "." \
|
|
TO_STRING(VERSION_MINOR) "." \
|
|
TO_STRING(VERSION_PATCH)
|
|
|
|
int main(void) {
|
|
printf("Version: %s\n", VERSION_STRING); // "Version: 1.2.3"
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
#define CLAY__STRINGIFY(x) #x
|
|
#define CLAY__VERSION_STRING \
|
|
CLAY__STRINGIFY(CLAY_VERSION_MAJOR) "." \
|
|
CLAY__STRINGIFY(CLAY_VERSION_MINOR) "." \
|
|
CLAY__STRINGIFY(CLAY_VERSION_PATCH)
|
|
|
|
// Results in version string "0.12.0"
|
|
```
|
|
|
|
---
|
|
|
|
## 15.9 Token Pasting (##)
|
|
|
|
Concatenate tokens to create new identifiers.
|
|
|
|
### Basic Token Pasting
|
|
|
|
```c
|
|
#define CONCAT(a, b) a##b
|
|
|
|
int main(void) {
|
|
int xy = 100;
|
|
int value = CONCAT(x, y); // Becomes: xy
|
|
printf("%d\n", value); // 100
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### Generate Variable Names
|
|
|
|
```c
|
|
#define VAR(name, index) name##index
|
|
|
|
int main(void) {
|
|
int VAR(value, 1) = 10; // int value1 = 10;
|
|
int VAR(value, 2) = 20; // int value2 = 20;
|
|
int VAR(value, 3) = 30; // int value3 = 30;
|
|
|
|
printf("%d %d %d\n", value1, value2, value3);
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### Generate Function Names
|
|
|
|
```c
|
|
#define DEFINE_GETTER(type, name) \
|
|
type get_##name(void) { \
|
|
return global_##name; \
|
|
}
|
|
|
|
int global_count = 10;
|
|
float global_value = 3.14f;
|
|
|
|
DEFINE_GETTER(int, count) // Creates: int get_count(void)
|
|
DEFINE_GETTER(float, value) // Creates: float get_value(void)
|
|
|
|
int main(void) {
|
|
printf("%d\n", get_count()); // 10
|
|
printf("%.2f\n", get_value()); // 3.14
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
**Clay Example** (from clay.h):
|
|
```c
|
|
// Generate array types for different types
|
|
#define CLAY__ARRAY_DEFINE(typeName, arrayName) \
|
|
typedef struct { \
|
|
int32_t capacity; \
|
|
int32_t length; \
|
|
typeName *internalArray; \
|
|
} arrayName;
|
|
|
|
// Creates multiple array types
|
|
CLAY__ARRAY_DEFINE(int32_t, Clay__int32_tArray)
|
|
CLAY__ARRAY_DEFINE(Clay_String, Clay__StringArray)
|
|
CLAY__ARRAY_DEFINE(Clay_RenderCommand, Clay__RenderCommandArray)
|
|
```
|
|
|
|
---
|
|
|
|
## 15.10 Predefined Macros
|
|
|
|
C provides built-in macros.
|
|
|
|
### Standard Predefined Macros
|
|
|
|
```c
|
|
#include <stdio.h>
|
|
|
|
int main(void) {
|
|
printf("File: %s\n", __FILE__); // Current file name
|
|
printf("Line: %d\n", __LINE__); // Current line number
|
|
printf("Date: %s\n", __DATE__); // Compilation date
|
|
printf("Time: %s\n", __TIME__); // Compilation time
|
|
printf("Function: %s\n", __func__); // Current function (C99)
|
|
|
|
#ifdef __STDC__
|
|
printf("Standard C: Yes\n");
|
|
#endif
|
|
|
|
#ifdef __STDC_VERSION__
|
|
printf("C Version: %ld\n", __STDC_VERSION__);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### Platform Predefined Macros
|
|
|
|
```c
|
|
int main(void) {
|
|
#ifdef _WIN32
|
|
printf("Windows\n");
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
printf("Linux\n");
|
|
#endif
|
|
|
|
#ifdef __APPLE__
|
|
printf("macOS\n");
|
|
#endif
|
|
|
|
#ifdef __x86_64__
|
|
printf("64-bit x86\n");
|
|
#endif
|
|
|
|
#ifdef __aarch64__
|
|
printf("64-bit ARM\n");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
### Compiler Predefined Macros
|
|
|
|
```c
|
|
int main(void) {
|
|
#ifdef __GNUC__
|
|
printf("GCC version: %d.%d.%d\n",
|
|
__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
printf("MSVC version: %d\n", _MSC_VER);
|
|
#endif
|
|
|
|
#ifdef __clang__
|
|
printf("Clang version: %s\n", __clang_version__);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 15.11 Macro Best Practices
|
|
|
|
### Rule 1: Always Use Parentheses
|
|
|
|
```c
|
|
// WRONG
|
|
#define MULTIPLY(a, b) a * b
|
|
int result = MULTIPLY(2 + 3, 4 + 5); // 2 + 3 * 4 + 5 = 19 (wrong!)
|
|
|
|
// RIGHT
|
|
#define MULTIPLY(a, b) ((a) * (b))
|
|
int result = MULTIPLY(2 + 3, 4 + 5); // (2 + 3) * (4 + 5) = 45 (correct!)
|
|
```
|
|
|
|
### Rule 2: Avoid Side Effects
|
|
|
|
```c
|
|
// DANGEROUS
|
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
|
|
int x = 5;
|
|
int max = MAX(x++, 10); // x incremented multiple times!
|
|
|
|
// SAFER - use inline function
|
|
static inline int max(int a, int b) {
|
|
return a > b ? a : b;
|
|
}
|
|
|
|
int x = 5;
|
|
int max_val = max(x++, 10); // x incremented once
|
|
```
|
|
|
|
### Rule 3: Use UPPERCASE for Macros
|
|
|
|
```c
|
|
// Makes it clear this is a macro
|
|
#define BUFFER_SIZE 256
|
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
|
|
// Not a macro
|
|
static inline int max(int a, int b) { return a > b ? a : b; }
|
|
```
|
|
|
|
### Rule 4: Prefer inline Functions
|
|
|
|
```c
|
|
// Macro (older style)
|
|
#define SQUARE(x) ((x) * (x))
|
|
|
|
// inline function (modern, preferred)
|
|
static inline int square(int x) {
|
|
return x * x;
|
|
}
|
|
```
|
|
|
|
**When to use macros:**
|
|
- ✅ Constants
|
|
- ✅ Conditional compilation
|
|
- ✅ Type-generic operations
|
|
- ✅ Code generation
|
|
|
|
**When to use inline functions:**
|
|
- ✅ Type-safe operations
|
|
- ✅ Complex logic
|
|
- ✅ Avoid side effects
|
|
|
|
---
|
|
|
|
## 15.12 Clay's Macro Usage
|
|
|
|
### Configuration Macros
|
|
|
|
```c
|
|
// User can override before including
|
|
#define CLAY_MAX_ELEMENT_COUNT 8192
|
|
#define CLAY_MAX_MEASURETEXT_CACHE_SIZE 16384
|
|
|
|
#define CLAY_IMPLEMENTATION
|
|
#include "clay.h"
|
|
```
|
|
|
|
### Initialization Macros
|
|
|
|
```c
|
|
// Compound literal initialization
|
|
#define CLAY__INIT(type) (type)
|
|
|
|
Clay_Color red = CLAY__INIT(Clay_Color) {
|
|
.r = 255, .g = 0, .b = 0, .a = 255
|
|
};
|
|
```
|
|
|
|
### String Macros
|
|
|
|
```c
|
|
// Create Clay_String from literal
|
|
#define CLAY_STRING(stringContents) \
|
|
(Clay_String){ \
|
|
.length = sizeof(stringContents) - 1, \
|
|
.chars = (stringContents) \
|
|
}
|
|
|
|
// Usage
|
|
Clay_String title = CLAY_STRING("My Application");
|
|
```
|
|
|
|
### Layout Macros
|
|
|
|
```c
|
|
// Convenient padding creation
|
|
#define CLAY_PADDING_ALL(amount) \
|
|
(Clay_Padding){amount, amount, amount, amount}
|
|
|
|
#define CLAY_PADDING(left, right, top, bottom) \
|
|
(Clay_Padding){left, right, top, bottom}
|
|
|
|
// Usage
|
|
Clay_Padding pad = CLAY_PADDING_ALL(16);
|
|
```
|
|
|
|
### ID Macros
|
|
|
|
```c
|
|
// Generate element IDs
|
|
#define CLAY_ID(label) \
|
|
Clay__HashString(CLAY_STRING(label), 0, 0)
|
|
|
|
#define CLAY_IDI(label, index) \
|
|
Clay__HashString(CLAY_STRING(label), index, 0)
|
|
|
|
// Usage
|
|
Clay_ElementId buttonId = CLAY_ID("SubmitButton");
|
|
Clay_ElementId itemId = CLAY_IDI("ListItem", i);
|
|
```
|
|
|
|
---
|
|
|
|
## 15.13 Complete Clay Macro Example
|
|
|
|
```c
|
|
#define CLAY_IMPLEMENTATION
|
|
#include "clay.h"
|
|
|
|
int main(void) {
|
|
// String macro
|
|
Clay_String title = CLAY_STRING("Clay Demo");
|
|
|
|
// Color macro
|
|
Clay_Color bgColor = CLAY__INIT(Clay_Color) {
|
|
.r = 200, .g = 200, .b = 200, .a = 255
|
|
};
|
|
|
|
// Padding macro
|
|
Clay_Padding padding = CLAY_PADDING_ALL(16);
|
|
|
|
// ID macro
|
|
Clay_ElementId rootId = CLAY_ID("Root");
|
|
|
|
// Use in layout
|
|
CLAY(CLAY_ID("Container"),
|
|
CLAY_LAYOUT({
|
|
.padding = CLAY_PADDING_ALL(8),
|
|
.sizing = {
|
|
.width = CLAY_SIZING_GROW(0),
|
|
.height = CLAY_SIZING_GROW(0)
|
|
}
|
|
}),
|
|
CLAY_RECTANGLE_CONFIG({
|
|
.color = bgColor
|
|
})) {
|
|
CLAY_TEXT(title, CLAY_TEXT_CONFIG({
|
|
.fontSize = 24,
|
|
.textColor = CLAY__INIT(Clay_Color){0, 0, 0, 255}
|
|
}));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 15.14 Key Concepts Learned
|
|
|
|
- ✅ Preprocessor runs before compilation
|
|
- ✅ #include inserts file contents
|
|
- ✅ #define creates macros
|
|
- ✅ Conditional compilation (#ifdef, #if)
|
|
- ✅ Header guards prevent multiple inclusion
|
|
- ✅ Function-like macros with parameters
|
|
- ✅ Multi-line macros with do-while(0)
|
|
- ✅ Stringification with #
|
|
- ✅ Token pasting with ##
|
|
- ✅ Predefined macros (__FILE__, __LINE__)
|
|
- ✅ Best practices and pitfalls
|
|
- ✅ Clay's effective macro usage
|
|
|
|
---
|
|
|
|
## Practice Exercises
|
|
|
|
1. Create a DEBUG macro that prints file, line, and function name
|
|
2. Write ARRAY_SIZE macro to get array length
|
|
3. Implement MIN3 and MAX3 macros for 3 values
|
|
4. Create a TYPEOF macro using _Generic (C11)
|
|
5. Build a FOR_EACH macro to iterate arrays
|
|
6. Design a BENCHMARK macro to time code execution
|
|
7. Create platform-specific file path macros
|
|
8. Implement a simple logging system with macros
|