Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ObjC blocks & openssl C callbacks

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.

like image 962
Dirk-Willem van Gulik Avatar asked May 04 '12 08:05

Dirk-Willem van Gulik


People also ask

What are blocks in OBJC?

Blocks are a language-level feature added to C, Objective-C and C++ which allow you to create distinct segments of code that can be passed around to methods or functions as if they were values. Blocks are Objective-C objects which means they can be added to collections like NSArray or NSDictionary.

How do you write blocks in Objective-C?

^blockName: Always remember that the block name is preceded by the ^ symbol. The block name can be any string you like, just like you name any other variable or method. Remember that both the ^ and the block name are enclosed in parentheses ().

What are blocks in Swift?

Closures in Swift Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.

What is @property in Objective-C?

The goal of the @property directive is to configure how an object can be exposed. If you intend to use a variable inside the class and do not need to expose it to outside classes, then you do not need to define a property for it. Properties are basically the accessor methods.

What is ID in Obj C?

“id” is a data type of object identifiers in Objective-C, which can be use for an object of any type no matter what class does it have. “id” is the final supertype of all objects.


1 Answers

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!

like image 103
CRD Avatar answered Oct 14 '22 21:10

CRD