|
4 days ago | |
---|---|---|
.. | ||
src | 1 month ago | |
README.md | 1 month ago | |
biome.json | 1 month ago | |
exclusions.txt | 1 month ago | |
package-lock.json | 1 month ago | |
package.json | 4 days ago | |
tsconfig.json | 1 month ago | |
tsfmt.json | 1 month ago |
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. The generator also builds inheritance maps and interface information for multi-language binding generation.
The code generator performs static analysis on the spine-cpp headers to automatically generate a C API that wraps the C++ classes. It handles:
The generator follows a multi-stage pipeline:
Type Extraction (type-extractor.ts
)
-ast-dump=json
to parse C++ headersspine-cpp-types.json
Type Processing (index.ts
)
Validation (checks.ts
)
Array Scanning (array-scanner.ts
)
Array<T>
usageIR Generation (ir-generator.ts
)
Code Writing (c-writer.ts
)
types.h
)Inheritance Analysis
The generator categorizes types for systematic conversion:
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)String
→ const char*
PropertyId
→ int64_t
Array<T>
→ spine_array_T
(specialized types)int
, float
, bool
, etc.float&
→ float*
codegen/
├── src/
│ ├── index.ts # Main entry point and orchestration
│ ├── type-extractor.ts # Clang AST parsing
│ ├── cpp-check.ts # C++ nullability analysis tool
│ ├── 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
├── dist/ # TypeScript compilation output
├── exclusions.txt # Type/method exclusions
├── spine-cpp-types.json # Extracted type information
├── nullable.md # C++ nullability analysis results
├── out.json # Debug output file
├── package.json # Node.js configuration
├── tsconfig.json # TypeScript configuration
├── tsfmt.json # TypeScript formatter configuration
├── biome.json # Biome linter configuration
└── node_modules/ # Dependencies
Generated files are output to ../src/generated/
:
skeleton.h
, skeleton.cpp
)types.h
- Forward declarations for all typesarrays.h/cpp
- Array specializations# Install dependencies
npm install
# Run the code generator
npx tsx src/index.ts
# Or export JSON for debugging
npx tsx src/index.ts --export-json
# The generated files will be in ../src/generated/
The codegen includes a tool to analyze spine-cpp for nullability patterns:
# Generate nullable.md with clickable links to methods with nullable inputs/outputs
npm run cpp-check
This tool identifies all methods that either:
The output nullable.md
contains clickable markdown links for easy navigation in VS Code. This is useful for cleaning up the spine-cpp API to use references vs pointers appropriately to signal nullability.
The generator automatically:
Primitive types are "pass-through".
C++ Type → C Type
─────────────────────────────────
int → int
float* → float*
const char* → const char*
bool → bool (stdbool.h)
size_t → size_t
C++ Type → C Type
─────────────────────────────────
Bone* → spine_bone
const Bone* → const spine_bone
Bone& → spine_bone
const Bone& → spine_bone
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
C++ Type → C Type
─────────────────────────────────
float& → float* (output param)
int& → int* (output param)
C++ Method → C Function
─────────────────────────────────────────
Skeleton::updateCache() → spine_skeleton_update_cache()
AnimationState::apply() → spine_animation_state_apply()
Bone::getX() → spine_bone_get_x()
The exclusions.txt
file controls what gets generated:
Exclude entire types from generation:
type: SkeletonClipping
type: Triangulator
Exclude specific methods:
method: AnimationState::setListener
method: AnimationState::addListener
Exclude only const or non-const versions:
method: BoneData::getSetupPose const
Allow type but prevent instantiation:
method: AtlasRegion::AtlasRegion
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
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)
The generator performs extensive validation to ensure correctness:
Detects methods with both const and non-const versions:
T& getValue(); // Non-const version
const T& getValue() const; // Const version
Rejects types with multiple pointer levels:
char** strings; // Not supported
void*** ptr; // Not supported
Detects when generated accessors would conflict with existing methods:
class Bone {
float x; // Would generate get_x/set_x
float getX(); // Conflicts with generated getter
};
Ensures generated function names don't collide with type names:
class BonePose { }; // → spine_bone_pose
class Bone {
void pose(); // → spine_bone_pose (conflict!)
};
Detects methods returning non-primitive types by value:
Color getColor(); // Cannot return objects by value in C
The generator automatically creates specialized array types for any Array<T>
found in the API:
Array<float> → spine_array_float
Array<int> → spine_array_int
Array<unsigned short> → spine_array_unsigned_short
Array<Bone*> → spine_array_bone
Array<Slot*> → spine_array_slot
Array<float*> → spine_array_float_ptr
Array<BlendMode> → spine_array_blend_mode
Array<PropertyId> → spine_array_property_id
Array<String>
- Use const char**
insteadArray<Array<T>>
- Nested arrays not supported// Header: skeleton.h
typedef struct spine_skeleton* spine_skeleton;
spine_skeleton spine_skeleton_create(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_create(spine_skeleton_data data) {
return (spine_skeleton) new (__FILE__, __LINE__) Skeleton((SkeletonData*)data);
}
void spine_skeleton_update_cache(spine_skeleton self) {
((Skeleton*)self)->updateCache();
}
// Header: blend_mode.h
#ifndef SPINE_SPINE_BLEND_MODE_H
#define SPINE_SPINE_BLEND_MODE_H
#ifdef __cplusplus
extern "C" {
#endif
typedef enum spine_blend_mode {
SPINE_BLEND_MODE_NORMAL = 0,
SPINE_BLEND_MODE_ADDITIVE,
SPINE_BLEND_MODE_MULTIPLY,
SPINE_BLEND_MODE_SCREEN
} spine_blend_mode;
#ifdef __cplusplus
}
#endif
#endif /* SPINE_SPINE_BLEND_MODE_H */
Arrays are generated as opaque types with complete CRUD operations. All arrays are consolidated into arrays.h
and arrays.cpp
.
// Header: arrays.h
SPINE_OPAQUE_TYPE(spine_array_float)
// Creation functions
spine_array_float spine_array_float_create(void);
spine_array_float spine_array_float_create_with_capacity(size_t initialCapacity);
// Memory management
void spine_array_float_dispose(spine_array_float array);
void spine_array_float_clear(spine_array_float array);
// Size and capacity operations
size_t spine_array_float_get_capacity(spine_array_float array);
size_t spine_array_float_size(spine_array_float array);
spine_array_float spine_array_float_set_size(spine_array_float array, size_t newSize, float defaultValue);
void spine_array_float_ensure_capacity(spine_array_float array, size_t newCapacity);
// Element operations
void spine_array_float_add(spine_array_float array, float inValue);
void spine_array_float_add_all(spine_array_float array, spine_array_float inValue);
void spine_array_float_clear_and_add_all(spine_array_float array, spine_array_float inValue);
void spine_array_float_remove_at(spine_array_float array, size_t inIndex);
// Search operations
bool spine_array_float_contains(spine_array_float array, float inValue);
int spine_array_float_index_of(spine_array_float array, float inValue);
// Direct buffer access
float *spine_array_float_buffer(spine_array_float array);
// Implementation: arrays.cpp
spine_array_float spine_array_float_create(void) {
return (spine_array_float) new (__FILE__, __LINE__) Array<float>();
}
void spine_array_float_dispose(spine_array_float array) {
delete (Array<float> *) array;
}
void spine_array_float_add(spine_array_float array, float inValue) {
Array<float> *_array = (Array<float> *) array;
_array->add(inValue);
}
float *spine_array_float_buffer(spine_array_float array) {
Array<float> *_array = (Array<float> *) array;
return _array->buffer();
}
Arrays are generated for all basic types (float
, int
, unsigned_short
, property_id
) and all object types used in collections throughout the API. The implementation directly casts the opaque handle to the underlying Array<T>*
type.
SpineObject
use location-based operator new
new (__FILE__, __LINE__)
for memory trackingdelete
on the C++ objectSpineObject
_create
, _create2
, _create3
obj.field.x
)_create
, _create2
, _create3
, etc._1
, _2
, _3
, etc._method
suffix to avoid constructor conflictsgetRTTI().instanceOf()
)WarningsCollector
"Unknown type: X"
"Multi-level pointers are not supported"
**
or more pointers"Array is not supported"
const char**
in C++ or exclude"No public constructors"
"Method/type name conflict"
"Multiple concrete inheritance detected"
spine-cpp-types.json
for extracted type information--export-json
flag to export inheritance and type information as JSONout.json
for debug output when troubleshootingThe codegen project includes several development tools and configurations:
biome.json
)tsfmt.json
)dist/
)out.json
)The project uses minimal dependencies for maximum compatibility:
@types/node
- Node.js type definitionstsx
- TypeScript execution enginetypescript-formatter
- Code formatting@biomejs/biome
- Fast linter for code quality