I would like to avoid the middle man and call Objective C directly from C
Currently I do this
This is called from a dispatch table in a pure C file
_ctx->mt_render_funcs.mtlEnd(_ctx);
Which calls this routine in a obj c file .m
void mtlEnd(MTRenderContext mt_ctx) { // Call the Objective-C method using Objective-C syntax [(__bridge id) mt_ctx->mt_render_funcs.mtlObj mtlEnd]; }
Which ends up here... in ObjC
#pragma mark mtlEnd
- (void)mtlEnd
{ // vertex buffer size_t size;
size = sizeof(Vertex4ColorNormalTex) * _ctx->vert_eng.current_vertex;
[_currentRenderEncoder setVertexBytes:_ctx->vert_eng.vertices length: size atIndex: VertexInputIndexVertices];
[_currentRenderEncoder drawPrimitives:(MTLPrimitiveType)_ctx->vert_eng.prim_type
vertexStart:0
vertexCount:_ctx->vert_eng.current_vertex];
}
It would simplify this to get rid of one call and call ObjC directly.
The other idea is I want to use GCD and put a lock / unlock on the call from C to ensure thread safety so I can use GCD to dispatch a thread to do the ObjC routines.
I want to stick with C as the foundation so it can be used directly from C or a FFI interface from other languages. But Metal works well in ObjC and I would prefer to use that.
Thanks ahead of time.
Calling Objective-C directly from C is certainly possible and can simplify your codebase by eliminating intermediate layers. Additionally, using Grand Central Dispatch (GCD) to manage thread safety and dispatch tasks to Objective-C contexts is a sound approach. Below, I'll outline how you can achieve this, maintaining thread safety and leveraging GCD effectively.
Direct Objective-C Function Call from C
Assuming you have an Objective-C method you want to call directly from C, you can use the Objective-C runtime functions to achieve this. Here's a step-by-step guide:
Objective-C Method Declaration
First, ensure your Objective-C method is declared properly. For example:
@interface Renderer : NSObject
- (void)mtlEnd;
@end
C Function to Call Objective-C Method
You can define a C function that uses the Objective-C runtime to call the mtlEnd method:
#include <objc/runtime.h>
void mtlEnd(MTRenderContext mt_ctx) { // Retrieve the class of the render context Class rendererClass = mt_ctx->mt_render_funcs.mtlObj.isa;
// Get the selector for the mtlEnd method
SEL selector = @selector(mtlEnd);
// Find the implementation of the method
IMP implementation = class_getInstanceMethod(rendererClass, selector);
// Call the method using the IMP
void (*func)(id, SEL) = (void (*)(id, SEL))implementation;
func(mt_ctx->mt_render_funcs.mtlObj, selector);
}
Thread Safety with GCD
To ensure thread safety when calling Objective-C code from C, you can use GCD to dispatch the call to a specific thread or queue. Here's how you can incorporate a lock using dispatch_queue_t:
#include <dispatch/dispatch.h>
// Create a serial dispatch queue for thread safety static dispatch_queue_t renderQueue = dispatch_queue_create("com.yourapp.renderQueue", DISPATCH_QUEUE_SERIAL);
void mtlEnd(MTRenderContext mt_ctx) { dispatch_async(renderQueue, ^{ // Retrieve the class of the render context Class rendererClass = mt_ctx->mt_render_funcs.mtlObj.isa;
// Get the selector for the mtlEnd method
SEL selector = @selector(mtlEnd);
// Find the implementation of the method
IMP implementation = class_getInstanceMethod(rendererClass, selector);
// Call the method using the IMP
void (*func)(id, SEL) = (void (*)(id, SEL))implementation;
func(mt_ctx->mt_render_funcs.mtlObj, selector);
});
}
Explanation
Objective-C Runtime: Functions like class_getInstanceMethod and IMP are used to dynamically invoke Objective-C methods from C. GCD for Thread Safety: By dispatching the method call to a serial queue (renderQueue), you ensure that only one thread executes the Objective-C code at a time, preventing race conditions. Serial Queue: DISPATCH_QUEUE_SERIAL ensures FIFO execution, maintaining the order of operations and protecting shared resources.
Integration
Ensure your C and Objective-C files are properly linked in your Xcode project. Compile with the necessary flags to support Objective-C runtime functions (usually -framework Foundation).
This approach allows you to maintain a C foundation for your application while efficiently leveraging Objective-C's capabilities, particularly for tasks like rendering with Metal, and ensures thread safety using GCD.