mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-06 07:14:55 +08:00
[c] Update codegen README.md
This commit is contained in:
parent
aac98324f2
commit
30d4cb9820
@ -1,6 +1,6 @@
|
||||
# Spine C API Code Generator
|
||||
|
||||
This TypeScript-based code generator automatically creates a C wrapper API for the Spine C++ runtime. It parses the spine-cpp headers and generates a complete C API with opaque types, following systematic type conversion rules.
|
||||
This TypeScript-based code generator automatically creates a C wrapper API for the Spine C++ runtime. It parses the spine-cpp headers using Clang's AST and generates a complete C API with opaque types, following systematic type conversion rules.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@ -8,363 +8,456 @@ This TypeScript-based code generator automatically creates a C wrapper API for t
|
||||
2. [Architecture](#architecture)
|
||||
3. [Type System](#type-system)
|
||||
4. [File Structure](#file-structure)
|
||||
5. [Generation Process](#generation-process)
|
||||
6. [Key Design Decisions](#key-design-decisions)
|
||||
7. [Exclusion System](#exclusion-system)
|
||||
8. [Array Specializations](#array-specializations)
|
||||
9. [Type Conversion Rules](#type-conversion-rules)
|
||||
10. [Running the Generator](#running-the-generator)
|
||||
5. [Usage](#usage)
|
||||
6. [Type Conversion Rules](#type-conversion-rules)
|
||||
7. [Exclusions System](#exclusions-system)
|
||||
8. [Validation Checks](#validation-checks)
|
||||
9. [Array Specializations](#array-specializations)
|
||||
10. [Generated Code Examples](#generated-code-examples)
|
||||
11. [Implementation Details](#implementation-details)
|
||||
12. [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Overview
|
||||
|
||||
The generator creates a C API that wraps the spine-cpp C++ runtime, allowing C programs to use Spine functionality. Key features:
|
||||
The code generator performs static analysis on the spine-cpp headers to automatically generate a C API that wraps the C++ classes. It handles:
|
||||
|
||||
- **Opaque Types**: All C++ classes are exposed as opaque pointers in C
|
||||
- **Automatic Memory Management**: Generates create/dispose functions
|
||||
- **Method Wrapping**: Converts C++ methods to C functions with proper type conversion
|
||||
- **Array Specializations**: Generates concrete array types for all Array<T> usage
|
||||
- **Systematic Type Handling**: Uses categorized type conversion instead of ad-hoc rules
|
||||
- Type conversions between C++ and C
|
||||
- Method wrapping with proper parameter marshaling
|
||||
- Memory management through constructors and destructors
|
||||
- Enum conversions
|
||||
- Array specializations for different element types
|
||||
- Field accessors (getters/setters) for public fields
|
||||
- Automatic validation and conflict detection
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Components
|
||||
The generator follows a multi-stage pipeline:
|
||||
|
||||
```
|
||||
codegen/
|
||||
├── src/
|
||||
│ ├── index.ts # Main entry point
|
||||
│ ├── types.ts # Type definitions and conversion
|
||||
│ ├── exclusions.ts # Exclusion system
|
||||
│ ├── type-extractor.ts # Automatic type extraction
|
||||
│ ├── array-scanner.ts # Array specialization scanner
|
||||
│ ├── file-writer.ts # File generation
|
||||
│ └── generators/
|
||||
│ ├── opaque-type-generator.ts # Opaque type declarations
|
||||
│ ├── constructor-generator.ts # Create/dispose functions
|
||||
│ ├── method-generator.ts # Method wrappers
|
||||
│ ├── enum-generator.ts # Enum conversions
|
||||
│ └── array-generator.ts # Array specializations
|
||||
├── exclusions.txt # Types/methods to exclude
|
||||
└── spine-cpp-types.json # Extracted type information
|
||||
```
|
||||
1. **Type Extraction** (`type-extractor.ts`)
|
||||
- Uses Clang's `-ast-dump=json` to parse C++ headers
|
||||
- Extracts all public members (methods, fields, constructors, destructors)
|
||||
- Handles template types and inheritance relationships
|
||||
- Outputs to `spine-cpp-types.json`
|
||||
|
||||
### Data Flow
|
||||
2. **Type Processing** (`index.ts`)
|
||||
- Loads extracted types and exclusions
|
||||
- Filters out template types, excluded types, and internal classes
|
||||
- Determines which types inherit from SpineObject
|
||||
- Validates that all types meet generation requirements
|
||||
|
||||
1. **Type Extraction**: `extract-spine-cpp-types.js` parses C++ headers → `spine-cpp-types.json`
|
||||
2. **Loading**: Generator loads JSON and exclusions
|
||||
3. **Filtering**: Excludes types based on rules (templates, abstracts, manual exclusions)
|
||||
4. **Generation**: Each generator processes types and creates C code
|
||||
5. **Writing**: Files are written to `src/generated/`
|
||||
3. **Validation** (`checks.ts`)
|
||||
- Detects const/non-const method conflicts
|
||||
- Identifies multi-level pointers
|
||||
- Finds field accessor conflicts
|
||||
- Checks for method/type name collisions
|
||||
- Validates return types
|
||||
|
||||
4. **Array Scanning** (`array-scanner.ts`)
|
||||
- Scans all types for `Array<T>` usage
|
||||
- Generates specialized array types for each element type
|
||||
- Handles primitive, pointer, and enum arrays
|
||||
|
||||
5. **IR Generation** (`ir-generator.ts`)
|
||||
- Converts C++ types to C intermediate representation
|
||||
- Generates wrapper methods with proper marshaling
|
||||
- Creates field accessors for public fields
|
||||
- Adds constructors and destructors
|
||||
|
||||
6. **Code Writing** (`c-writer.ts`)
|
||||
- Writes header files with C function declarations
|
||||
- Writes implementation files with C++ wrapper code
|
||||
- Generates array specialization files
|
||||
- Creates main include files (`types.h`, `spine-c.h`)
|
||||
|
||||
## Type System
|
||||
|
||||
### Type Categories
|
||||
|
||||
The generator classifies all C++ types into systematic categories:
|
||||
The generator categorizes types for systematic conversion:
|
||||
|
||||
1. **Primitives**: `int`, `float`, `double`, `bool`, `char`, `void`, `size_t`
|
||||
- Direct mapping (e.g., `bool` → `bool`)
|
||||
1. **Primitives**: Direct mapping (int, float, bool, size_t, etc.)
|
||||
2. **Special Types**: Custom conversions (String → const char*, PropertyId → int64_t)
|
||||
3. **Arrays**: Template specializations (Array<T> → spine_array_<type>)
|
||||
4. **Pointers**: Class pointers become opaque types
|
||||
5. **References**: Converted based on const-ness and type
|
||||
6. **Enums**: Prefixed and snake_cased
|
||||
7. **Classes**: Converted to opaque pointers with prefix
|
||||
|
||||
2. **Special Types**: String, function pointers, PropertyId
|
||||
- `String` → `const utf8 *`
|
||||
- `void *` → `spine_void`
|
||||
- `PropertyId` → `int64_t` (typedef'd to long long)
|
||||
### C++ to C Type Mapping
|
||||
|
||||
3. **Arrays**: `Array<T>` specializations
|
||||
- Generated as `spine_array_<element_type>`
|
||||
- Full API for each specialization
|
||||
The generator uses opaque pointers for all C++ classes:
|
||||
- `Skeleton*` → `spine_skeleton` (opaque pointer)
|
||||
- `const Skeleton*` → `const spine_skeleton`
|
||||
- `Skeleton&` → `spine_skeleton` (references become pointers)
|
||||
|
||||
4. **Pointers**: Type followed by `*`
|
||||
- Primitive pointers stay as-is (`float *`)
|
||||
- Class pointers become opaque (`Bone *` → `spine_bone`)
|
||||
### Special Types
|
||||
- `String` → `const char*`
|
||||
- `PropertyId` → `int64_t`
|
||||
- `Array<T>` → `spine_array_T` (specialized types)
|
||||
|
||||
5. **References**: Type followed by `&`
|
||||
- Const references: treated as value parameters
|
||||
- Non-const primitive references: output parameters (`float &` → `float *`)
|
||||
- Class references: converted to opaque types
|
||||
|
||||
6. **Enums**: Known spine enums
|
||||
- Prefixed with `spine_` and converted to snake_case
|
||||
|
||||
7. **Classes**: All other types
|
||||
- Assumed to be spine classes, converted to `spine_<snake_case>`
|
||||
|
||||
### Opaque Type Pattern
|
||||
|
||||
All C++ classes are exposed as opaque pointers:
|
||||
|
||||
```c
|
||||
// In types.h
|
||||
SPINE_OPAQUE_TYPE(spine_bone) // Expands to typedef struct spine_bone_wrapper* spine_bone
|
||||
|
||||
// In implementation
|
||||
spine_bone spine_bone_create() {
|
||||
return (spine_bone) new (__FILE__, __LINE__) Bone();
|
||||
}
|
||||
```
|
||||
### Primitive Types
|
||||
- Primitives pass through unchanged: `int`, `float`, `bool`, etc.
|
||||
- Non-const primitive references become pointers: `float&` → `float*`
|
||||
|
||||
## File Structure
|
||||
|
||||
### Generated Files
|
||||
|
||||
- **types.h**: Forward declarations for all types
|
||||
- All opaque type declarations
|
||||
- Includes for all enum headers
|
||||
- Includes arrays.h at the bottom
|
||||
|
||||
- **arrays.h/arrays.cpp**: Array specializations
|
||||
- Generated for all Array<T> found in spine-cpp
|
||||
- Complete API for each specialization
|
||||
|
||||
- **<type>.h/.cpp**: One pair per type
|
||||
- Header contains function declarations
|
||||
- Source contains implementations
|
||||
|
||||
- **spine-c.h**: Main header that includes everything
|
||||
|
||||
### Include Order
|
||||
|
||||
The main spine-c.h includes files in this order:
|
||||
1. base.h (basic definitions)
|
||||
2. types.h (all forward declarations)
|
||||
3. extensions.h (custom functionality)
|
||||
4. All generated type headers
|
||||
|
||||
This ensures all types are declared before use.
|
||||
|
||||
## Generation Process
|
||||
|
||||
### 1. Type Extraction
|
||||
|
||||
The generator automatically runs `extract-spine-cpp-types.js` if:
|
||||
- `spine-cpp-types.json` doesn't exist
|
||||
- Any spine-cpp header is newer than the JSON file
|
||||
|
||||
This script:
|
||||
- Parses all spine-cpp headers using tree-sitter
|
||||
- Extracts complete type information including inherited members
|
||||
- Resolves template inheritance
|
||||
- Marks abstract classes and templates
|
||||
|
||||
### 2. Type Filtering
|
||||
|
||||
Types are excluded if they are:
|
||||
- **Templates**: Detected by `isTemplate` field
|
||||
- **Abstract**: Have unimplemented pure virtual methods
|
||||
- **Internal utilities**: Array, String, HashMap, etc.
|
||||
- **Manually excluded**: Listed in exclusions.txt
|
||||
|
||||
### 3. Code Generation
|
||||
|
||||
For each included type:
|
||||
|
||||
#### Constructors
|
||||
- Generates `spine_<type>_create()` for default constructor
|
||||
- Generates `spine_<type>_create_with_<params>()` for parameterized constructors
|
||||
- Always generates `spine_<type>_dispose()` for cleanup
|
||||
|
||||
#### Methods
|
||||
- Getters: `spine_<type>_get_<property>()`
|
||||
- Setters: `spine_<type>_set_<property>()`
|
||||
- Other methods: `spine_<type>_<method_name>()`
|
||||
- Special handling for:
|
||||
- Vector return types (generate collection accessors)
|
||||
- RTTI methods (made static)
|
||||
- Const/non-const overloads (reported as errors)
|
||||
|
||||
#### Arrays
|
||||
- Scans all types for Array<T> usage
|
||||
- Generates specializations for each unique T
|
||||
- Filters out template placeholders (T, K)
|
||||
- Warns about problematic types (String, nested arrays)
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
### 1. Why Opaque Types?
|
||||
|
||||
C doesn't support classes or inheritance. Opaque pointers:
|
||||
- Hide implementation details
|
||||
- Prevent direct struct access
|
||||
- Allow polymorphism through base type pointers
|
||||
- Match C convention for handles
|
||||
|
||||
### 2. Why Generate Array Specializations?
|
||||
|
||||
C can't have template types. Options were:
|
||||
1. Use `void *` everywhere (loses type safety)
|
||||
2. Generate specializations (chosen approach)
|
||||
|
||||
Benefits:
|
||||
- Type safety in C
|
||||
- Better API documentation
|
||||
- Prevents casting errors
|
||||
|
||||
### 3. Why Systematic Type Classification?
|
||||
|
||||
Original code had many special cases. Systematic approach:
|
||||
- Reduces bugs from missed cases
|
||||
- Makes behavior predictable
|
||||
- Easier to maintain
|
||||
- Clear rules for each category
|
||||
|
||||
### 4. Why Exclude Const Methods?
|
||||
|
||||
C doesn't have const-correctness. When C++ has:
|
||||
```cpp
|
||||
T& getValue(); // for non-const objects
|
||||
const T& getValue() const; // for const objects
|
||||
```
|
||||
codegen/
|
||||
├── src/
|
||||
│ ├── index.ts # Main entry point and orchestration
|
||||
│ ├── type-extractor.ts # Clang AST parsing
|
||||
│ ├── types.ts # Type definitions and conversion logic
|
||||
│ ├── c-types.ts # C IR type definitions
|
||||
│ ├── array-scanner.ts # Array specialization detection
|
||||
│ ├── checks.ts # Validation checks
|
||||
│ ├── exclusions.ts # Exclusion handling
|
||||
│ ├── ir-generator.ts # C++ to C IR conversion
|
||||
│ ├── c-writer.ts # File generation
|
||||
│ └── warnings.ts # Warning collection
|
||||
├── exclusions.txt # Type/method exclusions
|
||||
├── spine-cpp-types.json # Extracted type information
|
||||
├── package.json # Node.js configuration
|
||||
├── tsconfig.json # TypeScript configuration
|
||||
└── generated/ # Output directory (temporary)
|
||||
```
|
||||
|
||||
C can only have one function name. We exclude const versions and expose non-const.
|
||||
Generated files are output to `../src/generated/`:
|
||||
- Individual files per type (e.g., `skeleton.h`, `skeleton.cpp`)
|
||||
- `types.h` - Forward declarations for all types
|
||||
- `arrays.h/cpp` - Array specializations
|
||||
- `spine-c.h` - Main include file
|
||||
|
||||
### 5. Why Static RTTI Methods?
|
||||
## Usage
|
||||
|
||||
RTTI objects are singletons in spine-cpp. Making getRTTI() static:
|
||||
- Reflects actual usage (Type::rtti)
|
||||
- Avoids unnecessary object parameter
|
||||
- Cleaner API
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
## Exclusion System
|
||||
|
||||
### exclusions.txt Format
|
||||
|
||||
```
|
||||
# Exclude entire types
|
||||
type: SkeletonClipping
|
||||
type: Triangulator
|
||||
|
||||
# Exclude specific methods
|
||||
method: AnimationState::setListener
|
||||
method: AnimationState::addListener
|
||||
|
||||
# Exclude const versions specifically
|
||||
method: BoneData::getSetupPose const
|
||||
npx tsx src/index.ts
|
||||
# The generated files will be in ../src/generated/
|
||||
```
|
||||
|
||||
### Exclusion Rules
|
||||
|
||||
1. **Type exclusions**: Entire type and all methods excluded
|
||||
2. **Method exclusions**: Specific methods on otherwise included types
|
||||
3. **Const-specific**: Can exclude just const or non-const version
|
||||
|
||||
## Array Specializations
|
||||
|
||||
### Scanning Process
|
||||
|
||||
1. Examines all members of non-excluded types
|
||||
2. Extracts Array<T> patterns from:
|
||||
- Return types
|
||||
- Parameter types
|
||||
- Field types
|
||||
3. Cleans element types (removes class/struct prefix)
|
||||
4. Categorizes as primitive/enum/pointer
|
||||
|
||||
### Generated API
|
||||
|
||||
For each Array<T>, generates:
|
||||
```c
|
||||
// Creation
|
||||
spine_array_float spine_array_float_create();
|
||||
spine_array_float spine_array_float_create_with_capacity(int32_t capacity);
|
||||
void spine_array_float_dispose(spine_array_float array);
|
||||
|
||||
// Element access
|
||||
float spine_array_float_get(spine_array_float array, int32_t index);
|
||||
void spine_array_float_set(spine_array_float array, int32_t index, float value);
|
||||
|
||||
// Array methods (auto-generated from Array type)
|
||||
size_t spine_array_float_size(spine_array_float array);
|
||||
void spine_array_float_clear(spine_array_float array);
|
||||
void spine_array_float_add(spine_array_float array, float value);
|
||||
// ... etc
|
||||
```
|
||||
|
||||
### Special Cases
|
||||
|
||||
- **String arrays**: Warned but skipped (should use const char**)
|
||||
- **Nested arrays**: Warned and skipped (Array<Array<T>>)
|
||||
- **PropertyId**: Treated as int64_t, not enum
|
||||
The generator automatically:
|
||||
- Detects when spine-cpp headers have changed
|
||||
- Regenerates only when necessary
|
||||
- Reports warnings and errors during generation
|
||||
|
||||
## Type Conversion Rules
|
||||
|
||||
### toCTypeName Function
|
||||
### Primitive Types
|
||||
Primitive types are "pass-through".
|
||||
|
||||
Implements systematic type conversion:
|
||||
|
||||
1. **Remove namespace**: Strip any `spine::` prefix
|
||||
2. **Check primitives**: Direct mapping via table
|
||||
3. **Check special types**: String, void*, function pointers
|
||||
4. **Check arrays**: Convert Array<T> to spine_array_*
|
||||
5. **Check pointers**: Handle based on pointed-to type
|
||||
6. **Check references**: Handle based on const-ness
|
||||
7. **Check enums**: Known enum list
|
||||
8. **Default to class**: Assume spine type
|
||||
|
||||
### Method Parameter Conversion
|
||||
|
||||
- **Input parameters**: C++ type to C type
|
||||
- **Output parameters**: Non-const references become pointers
|
||||
- **String parameters**: Create String objects from const char*
|
||||
- **Enum parameters**: Cast to C++ enum type
|
||||
|
||||
### Return Value Conversion
|
||||
|
||||
- **Strings**: Return buffer() as const char*
|
||||
- **References**: Take address and cast
|
||||
- **Enums**: Cast to C enum type
|
||||
- **Arrays**: Return as specialized array type
|
||||
|
||||
## Running the Generator
|
||||
|
||||
### Prerequisites
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
C++ Type → C Type
|
||||
─────────────────────────────────
|
||||
int → int
|
||||
float* → float*
|
||||
const char* → const char*
|
||||
bool → bool (stdbool.h)
|
||||
size_t → size_t
|
||||
```
|
||||
|
||||
### Build and Run
|
||||
|
||||
```bash
|
||||
npm run build # Compile TypeScript
|
||||
node dist/index.js # Run generator
|
||||
### Class Types
|
||||
```
|
||||
C++ Type → C Type
|
||||
─────────────────────────────────
|
||||
Bone* → spine_bone
|
||||
const Bone* → const spine_bone
|
||||
Bone& → spine_bone
|
||||
const Bone& → spine_bone
|
||||
```
|
||||
|
||||
### What Happens
|
||||
### Special Cases
|
||||
```
|
||||
C++ Type → C Type
|
||||
─────────────────────────────────
|
||||
String → const char*
|
||||
String& → const char*
|
||||
const String& → const char*
|
||||
PropertyId → int64_t
|
||||
Array<float> → spine_array_float
|
||||
Array<Bone*> → spine_array_bone
|
||||
```
|
||||
|
||||
1. Checks if type extraction needed (file timestamps)
|
||||
2. Runs extraction if needed
|
||||
3. Loads types and exclusions
|
||||
4. Filters types based on rules
|
||||
5. Generates code for each type
|
||||
6. Writes all files to src/generated/
|
||||
7. Updates main spine-c.h
|
||||
### Output Parameters
|
||||
```
|
||||
C++ Type → C Type
|
||||
─────────────────────────────────
|
||||
float& → float* (output param)
|
||||
int& → int* (output param)
|
||||
```
|
||||
|
||||
### Output
|
||||
### Function Naming
|
||||
```
|
||||
C++ Method → C Function
|
||||
─────────────────────────────────────────
|
||||
Skeleton::updateCache() → spine_skeleton_update_cache()
|
||||
AnimationState::apply() → spine_animation_state_apply()
|
||||
Bone::getX() → spine_bone_get_x()
|
||||
```
|
||||
|
||||
- Generates ~150 .h/.cpp file pairs
|
||||
- Creates arrays.h with ~30 specializations
|
||||
- All files include proper license headers
|
||||
- Organized by type for easy navigation
|
||||
## Exclusions System
|
||||
|
||||
## Maintenance
|
||||
The `exclusions.txt` file controls what gets generated:
|
||||
|
||||
### Type Exclusions
|
||||
Exclude entire types from generation:
|
||||
```
|
||||
type: SkeletonClipping
|
||||
type: Triangulator
|
||||
```
|
||||
|
||||
### Method Exclusions
|
||||
Exclude specific methods:
|
||||
```
|
||||
method: AnimationState::setListener
|
||||
method: AnimationState::addListener
|
||||
```
|
||||
|
||||
### Const-Specific Exclusions
|
||||
Exclude only const or non-const versions:
|
||||
```
|
||||
method: BoneData::getSetupPose const
|
||||
```
|
||||
|
||||
### Constructor Exclusions
|
||||
Allow type but prevent instantiation:
|
||||
```
|
||||
method: AtlasRegion::AtlasRegion
|
||||
```
|
||||
|
||||
### Field Accessor Exclusions
|
||||
Control field getter/setter generation:
|
||||
```
|
||||
field: AtlasRegion::names # Exclude both getter and setter
|
||||
field-get: SecretData::password # Exclude only getter
|
||||
field-set: Bone::x # Exclude only setter
|
||||
```
|
||||
|
||||
### Type-Wide Field Exclusions
|
||||
Exclude all field accessors for a type:
|
||||
```
|
||||
field: RenderCommand # No field accessors at all
|
||||
field-get: DebugData # No getters (write-only fields)
|
||||
field-set: RenderCommand # No setters (read-only fields)
|
||||
```
|
||||
|
||||
## Validation Checks
|
||||
|
||||
The generator performs extensive validation to ensure correctness:
|
||||
|
||||
### 1. Const/Non-Const Conflicts
|
||||
Detects methods with both const and non-const versions:
|
||||
```cpp
|
||||
T& getValue(); // Non-const version
|
||||
const T& getValue() const; // Const version
|
||||
```
|
||||
|
||||
### 2. Multi-Level Pointers
|
||||
Rejects types with multiple pointer levels:
|
||||
```cpp
|
||||
char** strings; // Not supported
|
||||
void*** ptr; // Not supported
|
||||
```
|
||||
|
||||
### 3. Field Accessor Conflicts
|
||||
Detects when generated accessors would conflict with existing methods:
|
||||
```cpp
|
||||
class Bone {
|
||||
float x; // Would generate get_x/set_x
|
||||
float getX(); // Conflicts with generated getter
|
||||
};
|
||||
```
|
||||
|
||||
### 4. Method/Type Name Conflicts
|
||||
Ensures generated function names don't collide with type names:
|
||||
```cpp
|
||||
class BonePose { }; // → spine_bone_pose
|
||||
class Bone {
|
||||
void pose(); // → spine_bone_pose (conflict!)
|
||||
};
|
||||
```
|
||||
|
||||
### 5. Value Return Types
|
||||
Detects methods returning non-primitive types by value:
|
||||
```cpp
|
||||
Color getColor(); // Cannot return objects by value in C
|
||||
```
|
||||
|
||||
## Array Specializations
|
||||
|
||||
The generator automatically creates specialized array types for any `Array<T>` found in the API:
|
||||
|
||||
### Primitive Arrays
|
||||
```cpp
|
||||
Array<float> → spine_array_float
|
||||
Array<int> → spine_array_int
|
||||
Array<unsigned short> → spine_array_unsigned_short
|
||||
```
|
||||
|
||||
### Pointer Arrays
|
||||
```cpp
|
||||
Array<Bone*> → spine_array_bone
|
||||
Array<Slot*> → spine_array_slot
|
||||
Array<float*> → spine_array_float_ptr
|
||||
```
|
||||
|
||||
### Enum Arrays
|
||||
```cpp
|
||||
Array<BlendMode> → spine_array_blend_mode
|
||||
Array<PropertyId> → spine_array_property_id
|
||||
```
|
||||
|
||||
### Unsupported Arrays
|
||||
- `Array<String>` - Use `const char**` instead
|
||||
- `Array<Array<T>>` - Nested arrays not supported
|
||||
- Arrays with const elements
|
||||
|
||||
## Generated Code Examples
|
||||
|
||||
### Class Wrapper
|
||||
```c
|
||||
// Header: skeleton.h
|
||||
typedef struct spine_skeleton* spine_skeleton;
|
||||
|
||||
spine_skeleton spine_skeleton_new(spine_skeleton_data data);
|
||||
void spine_skeleton_dispose(spine_skeleton self);
|
||||
void spine_skeleton_update_cache(spine_skeleton self);
|
||||
float spine_skeleton_get_x(const spine_skeleton self);
|
||||
void spine_skeleton_set_x(spine_skeleton self, float value);
|
||||
|
||||
// Implementation: skeleton.cpp
|
||||
spine_skeleton spine_skeleton_new(spine_skeleton_data data) {
|
||||
return (spine_skeleton) new (__FILE__, __LINE__) Skeleton((SkeletonData*)data);
|
||||
}
|
||||
|
||||
void spine_skeleton_update_cache(spine_skeleton self) {
|
||||
((Skeleton*)self)->updateCache();
|
||||
}
|
||||
```
|
||||
|
||||
### Enum Wrapper
|
||||
```c
|
||||
// Header: blend_mode.h
|
||||
typedef enum spine_blend_mode {
|
||||
SPINE_BLEND_MODE_NORMAL = 0,
|
||||
SPINE_BLEND_MODE_ADDITIVE = 1,
|
||||
SPINE_BLEND_MODE_MULTIPLY = 2,
|
||||
SPINE_BLEND_MODE_SCREEN = 3
|
||||
} spine_blend_mode;
|
||||
|
||||
// Implementation: blend_mode.cpp
|
||||
spine_blend_mode spine_blend_mode_from_cpp(BlendMode value) {
|
||||
return (spine_blend_mode)value;
|
||||
}
|
||||
|
||||
BlendMode spine_blend_mode_to_cpp(spine_blend_mode value) {
|
||||
return (BlendMode)value;
|
||||
}
|
||||
```
|
||||
|
||||
### Array Specialization
|
||||
```c
|
||||
// Header: array_float.h
|
||||
typedef struct spine_array_float* spine_array_float;
|
||||
|
||||
spine_array_float spine_array_float_new(int32_t capacity);
|
||||
void spine_array_float_dispose(spine_array_float self);
|
||||
int32_t spine_array_float_get_size(const spine_array_float self);
|
||||
float spine_array_float_get(const spine_array_float self, int32_t index);
|
||||
void spine_array_float_set(spine_array_float self, int32_t index, float value);
|
||||
|
||||
// Implementation: array_float.cpp
|
||||
struct spine_array_float {
|
||||
Array<float> data;
|
||||
};
|
||||
|
||||
spine_array_float spine_array_float_new(int32_t capacity) {
|
||||
auto* arr = new (__FILE__, __LINE__) spine_array_float();
|
||||
arr->data.setCapacity(capacity);
|
||||
return arr;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Memory Management
|
||||
- All C++ objects inheriting from `SpineObject` use location-based `operator new`
|
||||
- Constructors use `new (__FILE__, __LINE__)` for memory tracking
|
||||
- Destructors call `delete` on the C++ object
|
||||
- Array types are wrapped in structs to maintain C++ semantics
|
||||
|
||||
### Constructor Generation
|
||||
- Only generates constructors for non-abstract classes
|
||||
- Only generates constructors for classes inheriting from `SpineObject`
|
||||
- Requires at least one public constructor or explicit exclusion
|
||||
- Constructor overloads are numbered: `_new`, `_new2`, `_new3`
|
||||
|
||||
### Field Accessor Generation
|
||||
- Generates getters for all non-static public fields
|
||||
- Generates setters for non-const, non-reference fields
|
||||
- Uses direct field access, not C++ getter/setter methods
|
||||
- Handles nested field access (e.g., `obj.field.x`)
|
||||
|
||||
### Method Overloading
|
||||
- Constructor overloads are numbered: `_new`, `_new2`, `_new3`
|
||||
- Other overloads must be excluded (C doesn't support overloading)
|
||||
- Const/non-const conflicts are detected and reported
|
||||
|
||||
### RTTI Handling
|
||||
- Uses Spine's custom RTTI system (`getRTTI().instanceOf()`)
|
||||
- No C++ RTTI or exceptions are used
|
||||
- RTTI checks are performed in generated code where needed
|
||||
|
||||
### Warning System
|
||||
- Collects non-fatal issues during generation
|
||||
- Reports abstract classes, missing constructors, etc.
|
||||
- Warnings don't stop generation but are reported at the end
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Errors
|
||||
|
||||
1. **"Unknown type: X"**
|
||||
- The type is not a primitive and not in the extracted types
|
||||
- Solution: Add to exclusions or check spelling
|
||||
|
||||
2. **"Multi-level pointers are not supported"**
|
||||
- Type contains `**` or more pointers
|
||||
- Solution: Refactor C++ code or exclude
|
||||
|
||||
3. **"Array<String> is not supported"**
|
||||
- String arrays need special handling
|
||||
- Solution: Use `const char**` in C++ or exclude
|
||||
|
||||
4. **"No public constructors"**
|
||||
- Class has no public constructors for instantiation
|
||||
- Solution: Add public constructor or exclude
|
||||
|
||||
5. **"Method/type name conflict"**
|
||||
- Generated function name collides with a type name
|
||||
- Solution: Rename method or exclude
|
||||
|
||||
### Debugging Tips
|
||||
|
||||
1. Check `spine-cpp-types.json` for extracted type information
|
||||
2. Look for "Excluding" messages in console output
|
||||
3. Verify inheritance with "inherits from SpineObject" messages
|
||||
4. Array specializations are listed with element type mapping
|
||||
5. Check warnings at the end of generation for issues
|
||||
|
||||
### Adding New Types
|
||||
|
||||
1. No action needed - automatically detected from spine-cpp
|
||||
1. Ensure the type is in spine-cpp headers
|
||||
2. Remove from exclusions.txt if previously excluded
|
||||
3. Check that all dependent types are included
|
||||
4. Run generator and fix any reported issues
|
||||
5. Verify generated code compiles
|
||||
|
||||
### Excluding Types/Methods
|
||||
### Performance Considerations
|
||||
|
||||
1. Add to exclusions.txt
|
||||
2. Regenerate
|
||||
|
||||
### Changing Type Mappings
|
||||
|
||||
1. Update toCTypeName in types.ts
|
||||
2. Follow systematic categories
|
||||
|
||||
### Debugging
|
||||
|
||||
- Check spine-cpp-types.json for extracted data
|
||||
- Look for warnings in console output
|
||||
- Verify exclusions are applied correctly
|
||||
- Check generated files for correctness
|
||||
- Type extraction uses Clang AST parsing (slow but accurate)
|
||||
- File generation is parallelized where possible
|
||||
- Array scanning happens after type filtering for efficiency
|
||||
- Validation checks run before generation to fail fast
|
||||
- Incremental generation avoids regenerating unchanged files
|
||||
Loading…
x
Reference in New Issue
Block a user