ObjC blocks & openssl C callbacks
block, callback, ios, objective-c, openssl
Solution
All code is just typed into this answer, test carefully before using!
Function pointers and blocks are not the same thing; the former is just a reference to code, the latter is a closure containing both code and an environment; they are not trivially interchangeable.
You can of course use function pointers in Objective-C, so that is your first option.
If you wish to use blocks then you need to find a way to wrap a block and pass it as a function reference...
The definition of `RSA_generate_key` is:
RSA *RSA_generate_key(int num,
unsigned long e,
void (*callback)(int,int,void *),
void *cb_arg);
The fourth argument can be anything and is passed as the third argument to the callback; this suggests we could pass the block along with a pointer to a C function which calls it:
typedef void (^BlockCallback)(int,int);
static void callback(int p, int n, void *anon)
{
BlockCallback theBlock = (BlockCallback)anon; // cast the void * back to a block
theBlock(p, n); // and call the block
}
- (void) genReq:(BlockCallback)progressCallback
completionCallback:(void (^)())completionCallback
{
.....
// pass the C wrapper as the function pointer and the block as the callback argument
rsa = RSA_generate_key(bits, RSA_F4, callback, (void *)progressCallback);
assert(EVP_PKEY_assign_RSA(pkey,rsa));
rsa = NULL;
....
completionCallback();
}
And to invoke:
[self genReq:^(int p, int n)
{
hud.progress = --heuristic to guess where we are --
}
completionCallback:^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
];
Whether you need any bridge casts (for ARC) is left as an exercise!
Problem
Have a simple one-off tasks which needs a progress bar. OpenSSL has a useful callback which one can use for that: ``` rsa=RSA_generate_key(bits,RSA_F4,progressCallback,NULL); ``` with ``` static void callback(int p, int n, void *arg) { .. stuff ``` However I want to call this from ObjectiveC without too much ado: ``` MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; hud.mode = MBProgressHUDModeAnnularDeterminate; hud.labelText = @"Generating CSR"; [self genReq:^(int p,int n,void *arg) { hud.progress = --heuristic to guess where we are -- } completionCallback:^{ [MBProgressHUD hideHUDForView:self.view animated:YES]; }]; ``` With Genrec: as an objC method: ``` -(void)genReq:(void (^)(int,int,void *arg))progressCallback completionCallback:(void (^)())completionCallback { ..... rsa=RSA_generate_key(bits,RSA_F4,progressCallback,NULL); assert(EVP_PKEY_assign_RSA(pkey,rsa)); rsa=NULL; .... completionCallback(); } ``` Now completionCallback(); works splendidly and as expected. But I get a compiler warning/error which I cannot quell for the progress callback: ``` Passing 'void (^__strong)(int, int, void *)' to parameter of incompatible type 'void (*)(int, int, void *)' ``` So am curious -- what is the appropriate way to do this ? Thanks, Dw.