How to pass objective-c function as a callback to C function? - ios

I want to call a c function from objective-c and pass objective-c function as a callback
the problem is this function has a callback as parameter, so I have to pass objective-c function as a call back to c function
here is the header of the c function
struct mg_context *mg_start(const struct mg_callbacks *callbacks,
void *user_data,
const char **configuration_options);
here is where I try to call it
- (void)serverstarted
{
NSLog(#"server started");
}
- (IBAction)startserver:(id)sender {
NSLog(#"server should start");
const char *options[] =
{
"document_root", "www",
"listening_ports", "8080",
NULL
};
mg_start(serverstarted(), NULL, options);
}
I have tried several ways to do it and searched the web to just get a clue how to do it but with not luck
here is the library I am incuding in my code
https://github.com/valenok/mongoose

Your chief problem is the first parameter to mg_start(), which is described in the declaration as const struct mg_callbacks *callbacks. You are trying pass a pointer to a function. (Actually you are trying to pass the result of a call to that function, which is even further from the mark.) That isn't what it says: it says a pointer to a struct (in particular, an mg_callbacks struct).
The example code at https://github.com/valenok/mongoose/blob/master/examples/hello.c shows you how to configure this struct. You have to create the struct and put the pointer to the callback function inside it. Then you pass the address of that struct.
Other problems with your code: your callback function itself is all wrong:
- (void)serverstarted
{
NSLog(#"server started");
}
What's wanted here is a C function declared like this: int begin_request_handler(struct mg_connection *conn), that is, it takes as parameter a pointer to an mg_connection struct. Your serverstarted not only doesn't take that parameter, it isn't even a C function! It's an Objective-C method, a totally different animal. Your use of the term "Objective-C function" in your title and your question is misleading; C has functions, Objective-C has methods. No Objective-C is going to be used in the code you'll be writing here.
What I suggest you do here is to copy the hello.c example slavishly at first. Then modify the content / names of things slowly and bit by bit to evolve it to your own code. Of course learning C would also help, but you can probably get by just by copying carefully.

As matt already said, you cannot pass an Objective-C method as callback where a C function
is expected. Objective-C methods are special functions, in particular the receiver ("self")
is implicitly passed as first argument to the function.
Therefore, to use an Objective-C method as request handler, you need an (intermediate) C function as handler and you have to pass self to that function, using the user_data argument. The C function can then call the Objective-C method:
// This is the Objective-C request handler method:
- (int)beginRequest:(struct mg_connection *)conn
{
// Your request handler ...
return 1;
}
// This is the intermediate C function:
static int begin_request_handler(struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn);
// Cast the "user_data" back to an instance pointer of your class:
YourClass *mySelf = (__bridge YourClass *)request_info->user_data;
// Call instance method:
return [mySelf beginRequest:conn];
}
- (IBAction)startserver:(id)sender
{
struct mg_callbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler;
const char *options[] =
{
"document_root", "www",
"listening_ports", "8080",
NULL
};
// Pass "self" as "user_data" argument:
mg_start(&callbacks, (__bridge void *)self, options);
}
Remarks:
If you don't use ARC (automatic reference counting) then you can omit the (__bridge ...)
casts.
You must ensure that the instance of your class ("self")
is not deallocated while the server is running. Otherwise the YourClass *mySelf
would be invalid when the request handler is called.

Related

Objective C - Called object type 'BOOL' (aka 'bool') is not a function or function pointer

Working on a legacy hybrid iOS project. Created one new Swift util class in ConsentManager.swift, like below,
import Foundation
public class ConsentManager: NSObject {
#objc static let sharedInstance = ConsentManager()
#objc private override init() {}
#objc public func isDataPermissionConsentRequired() -> Bool
{
…
return value; // based on logic
}
}
Called the method from another objc class, ConsentChecker.m like,
#interface ConsentChecker ()
{
}
#end
#implementation ConsentChecker
-(void)checkConsent {
// GETTING ERROR IN THE FOLLOWING LINE
if (ConsentManager.sharedInstance.isDataPermissionConsentRequired()) {
…
}
}
#end
Getting compiler error:
Called object type 'BOOL' (aka 'bool') is not a function or function pointer
Why and how to resolve it?
The reason you're hitting this is that methods in Objective-C which take no arguments may be called implicitly using dot syntax similar to Swift's, but not exactly like it. A method declared like
// Inside of SomeClass
- (BOOL)someMethod { /* return something */ }
can be called either as
SomeClass *instance = ...
// Traditional Obj-C syntax:
BOOL value = [instance someMethod];
or
// Dot syntax accessor:
BOOL value = instance.someMethod;
Note that the dot syntax version does not use parentheses to denote the call. When you add parentheses like you would in Swift, Obj-C determines that you are trying to call the returned value from the method as if it were a function:
instance.someMethod();
// equivalent to:
BOOL value = [instance someMethod];
value(); // <- Called object type 'BOOL' (aka 'bool') is not a function or function pointer
You cannot call a BOOL like you can a function, hence the error.
#Dávid offers the more traditional Obj-C syntax for calling this method, but alternatively, you can simply drop the parentheses from your call:
if (ConsentManager.sharedInstance.isDataPermissionConsentRequired) {
Objective-C-ism note:
Dot syntax is most idiomatically used for method calls which appear like properties (e.g. boolean accessors like your isDataPermissionConsentRequired), even if the method might need to do a little bit of work to return that value (think: computed properties in Swift).
For methods which perform an action, or which return a value but might require a significant amount of work, traditional method call syntax is typically preferred:
// Prefer:
[instance doTheThing];
NSInteger result = [instance performSomeExpensiveCalculation];
// over:
instance.doTheThing;
NSInteger result = instance.performSomeExpensiveCalculation;
The Obj-C syntax for executing methods is different from Swift's dot syntax.
This is the correct syntax:
if ([ConsentManager.sharedInstance isDataPermissionConsentRequired]) {
If u want to call swift function on obj-c class you use to obj-c syntax
Correct Syntax is:
if ([ConsentManager.sharedInstance isDataPermissionConsentRequired]) {
// Write logic here
}

Add C function as value to NSDictionary

Upon receiving an NSString, I would like to call a specific code block. I figured an NSDictionary would be best for associating these. Simplified, I'm using something like:
MyProtocol.h:
#protocol MyProtocol <NSObject>
typedef void (^Handler)(id<MyProtocol> obj, id data);
#end
MyClass.h:
#interface MyClass : NSObject <MyProtocol>
- (void)aMethodWithString:(NSString *)string andData:(id)data;
#end
MyClass.m:
#interface MyClass ()
void myCommandHandler(id<MyProtocol> obj, id data); // matches signature defined in protocol
#end
#implementation MyClass
void myCommandHandler(id<MyProtocol> obj, id data)
{
// ...
}
- (void)aMethodWithString:(NSString *)string andData:(id)data
{
static NSDictionary<NSString *, Handler> *handler;
static dispatch_once_t onceToken;
// don't allocate this dictionary every time the function is called
dispatch_once(&onceToken,
^{
handler =
#{
#"MyCommand":myCommandHandler,
};
});
// ... error checking, blah blah ...
Handler block;
if ((block = handler[string]))
{ block(self, data); }
}
#end
Using this, I get an error in the dictionary literal construction:
Collection element of type 'void (__strong id<MyProtocol>, __strong id)' is not an Objective-C object`
So how can I include a C function or block reference in the dictionary? There will be quite a few larger complex functions to be defined, so it would be very much preferred to not have all of them defined inside the dictionary literal itself (a technique I know will work).
--
Also, I'm not sure what's considered proper style here: (1) I originally had the dictionary declaration outside of any method body at the file scope (without the dispatch_once(...)) which generates the same error, but I thought maybe it would be easier for others to see what's going on by (2) including it in the only method that uses that dictionary. Is one style preferred over the other for any reason?
Your Handler type is a block type, not a function pointer type. If you had declared it using * instead of ^, it would be a function pointer type.
Your myCommandHandler function is, of course, a function, not a block. Your comment is wrong. It does not match type Handler.
Function pointers are not Objective-C object pointers. Functions are not objects. Blocks are Objective-C objects, but you're not actually using any blocks here. (You've just declared a typedef for one, but you're not actually using it.)
You could use blocks and store them in your dictionary. The blocks could either contain the desired code or call a function or method which does.
A C function address is not an Objective-C object, and an NSDictionary can only store the latter.
A C function address is just a pointer, to wrap a C pointer as an object you use NSValue and its class method valueWithPointer.
To get the pointer value back from the NSValue object you use the instance method pointerValue. Before you can use the extracted pointer you must cast it to your function type.
HTH

How to save Obj-C block in a C struct?

I want to call a C function from my ObjC. I am passing a C function reference (defined in ObjC) in a C struct so that C can call that function. I also want to pass the reference to a completion-block to C, so that when I get a callback, I can call that completion-block. But I do not know how to implement that. I get different errors based on the different typecasting I tried.
//Abc.m
void myCallback(MyData *data)
{
//I get the control here!
//((__bridge void *)(data->completionBlock))([NSString stringWithCString:data->json]); //how to call the completion block?
}
- (void)myMethod:(NSString)input
completion:(void (^)(NSString * _Nullable response))completionBlock
{
MyData *data = malloc(sizeof(MyData));
data->myCallback = myCallback;
data->completionBlock = (__bridge void *)(completionBlock);//is this correct?
cFunction(data);
}
//Xyz.c
typedef struct
{
char *json;
void (*myCallback)(void *response);
void *completionBlock;
} MyData;
void cFunction(MyData *data)
{
data->json = "some response";
(data->myCallback)(data);
}
There are two issues to consider:
Casting: You need to cast to appropriate types, e.g. your commented out (__bridge void *)(data->completionBlock) doesn't give you back a block type so the compiler will reject the call.
Ownership: Blocks are just objects in Objective-C and are managed by ARC for you. In C blocks are manually managed. You must ensure that the block you pass into your C structure will not be released by ARC, and having used it you must ensure that it is freed.
Following your code design let's first define a type to make things easier:
typedef void (^CompletionBlock)(NSString * _Nullable response);
With that your myMethod function starts as before:
- (void)myMethod:(NSString*)input
completion:(CompletionBlock)completionBlock
{
MyData *data = malloc(sizeof(MyData));
data->myCallback = myCallback;
Now you must store your block into your struct while making sure ARC does not release it on the Objective-C side. To do that you use __bridge_retain which returns a retained reference to the block, your code will be responsible for balancing that retain. That can either be done in your C code or you can transfer the ownership back to ARC and let it take care of it. So the remainder of myMethod is:
data->completionBlock = (__bridge_retained void *)(completionBlock);
cFunction(data);
}
Now your cFunction just calls your myCallBack function so in this case there is no need to change anything there.
Now to your myCallBack, first we fix the type mismatch and define it to take a void * and then recover the MyData *:
void myCallback(void *response)
{
MyData *data = response;
Now we need to recover the block. We could just cast it to the block type, but that would leave us with the job of freeing it (using Block_release()) after we've used it; however we can use __bridge_transfer to hand back ownership to ARC so it will manage it:
CompletionBlock completionBlock = (__bridge_transfer CompletionBlock)data->completionBlock;
Now we get the string out and convert it to an NSString:
NSString *result = [NSString stringWithCString:data->json encoding:NSUTF8StringEncoding];
And then free the malloc'ed wrapper:
free(data);
Finally we call the block:
completionBlock(result);
}
The above followed your design, but there is no need to have your C function call another C function in your Objective-C file to call the block - blocks are a C language feature and supported in .c files by Clang. You can just cast data->completionBlock to the block type, invoke it, and then use Block_release() to free the block.
Further as blocks are C types you can type the struct field completionBlock with a block type and remove a lot of casts (but those cases cost nothing at runtime).
HTH
__bridge means to use c style pointer, its behaviour like assign or __unsafe_unretained.
You use it mean data->completionBlock just point to completionBlock without retain, so you may crash when completionBlock has been released.
If you wanna access block or objective-c object in struct with ARC, you have to declare these with __unsafe_unretained and manager their life by yourself.
typedef struct {
char *json;
__unsafe_unretained NSString *name;
__unsafe_unretained CompletionBlock block;
} SampleStruct;

Programming Asynchronous Delegate methods in Swift

I have an application which talks to a device over a BLE UART connection.
When data is received, a didReceiveData() delegate is called.
Here I need determine who called the data and trigger the corresponding method (of another delegate).
I was planning on creating a connections dictionary keyed by a connectionID string created when the connection is established, along with a selector to the callback (may not always be supplied).
class Connection: NSObject {
var selectr: Selector
var dataString: String?
init(selectR:Selector,dString:String) {
selectr = selectR
dataString = dString
}
}
connections[String:Connection]()
func sendData(dataString:String,callbackSelector:Selector){
con = Connection(selectR: callbackSelector, dString:"")
connections[cid] = con
}
...
When calling:
let sel = Selector(anotherDelegate?.didReceiveLocation(""))
self.sendData("sendMe",Selector(anotherDelegate?.didReceiveLocation))
I get a few errors doing this, first a Type NSData does not conform to protocol StringLiteralConvertible. NSData referring to the argument of didReceiveLocation.
The second is on the self.sendData line: Cannot invoke 'init' with an argument list of type (StringLiteralConvertible,Selector).
Does this approach make sense? How can I store the callback method of another delegate in a dictionary or other struct to make it accessible from the didReceiveData delegate method?
Selectors are so Objective-C... why not just use a closure?
To declare a dictionary with String as keys and functions that take a String as a parameter and return nothing:
connections = [String:String->()]()
To declare a function that takes a closure with a single String argument and no return value, use something like:
func sendData(dataString:String, callback:String->()) {
...
connections[cid] = callback
...
}
To invoke that closure later, you can call it as a subroutine (in this case after performing a dictionary lookup, using optional chaining in case it hasn't been assigned yet):
func didReceiveData(...) {
...
connections[cid]?(response)
}
Then to call the sendData routine declared above and pass in a closure using one of the several abbreviated syntaxes:
self.sendData("sendMe") { response in
...
}
Note that this is actually short-hand for:
self.sendData("sendMe", callback:{ response:String in
...
})
Note that this mechanism can also be used by an Objective-C caller, since closures map more or less directly to Objective-C blocks.
For more information on closures, their definition and invocation, I'd strongly recommend downloading the free Swift book from the iTunes book store.

Send javascript function to objective-C using JavascriptCore

I'm trying to send a Javascript function object to Objective-C via JavascriptCore, leveraging the JSExport protocol. I have a function declared in Objective-C, conforming to JSExport as follows:
(class View)
+ (void) newWithFunc:(id)func
{
NSLog(#" %# ", func);
}
After declaring this class, I try to call the function above with a Javascript function object as a parameter
JSValue *val;
val = [context evaluateScript:#"var mufunc = function() { self.value = 10; };"];
val = [context evaluateScript:#"mufunc;"];
NSLog(#" %#", val); //Prints as 'function() { self.value = 10; }', seems correct.
val = [context evaluateScript:#"var view = View.newWithFunc(mufunc);"];
When the last call is made, the parameter sent to my Objective-C method is of type 'NSDictionary', which doesn't seem very valuable if what I would like to do is call that function from Objective-C at a later point in time. Is this possible with JavascriptCore?
Please mark Tayschrenn's answer as correct. I don't know how he knew this or where it's documented, but this is what I figured out by trial and error:
- (void)newWithFunc: (JSValue*)func
{
[func callWithArguments:#[]]; // will invoke js func with no params
}
Declaring the parameter (id)func apparently causes the javascript-cocoa bridge to convert it to an NSDictionary (as you noticed), rendering it unusable as a callable JSValue.
This is definitely possible, but you need to change newWithFunc: to accept a JSValue* rather than plain id. The reason is that JSValue* is a special type for JavaScriptCore - it won't try to convert the JS value to its native equivalent but rather wrap it in a JSValue and pass it on.

Resources