Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

The cblock Macro Does Not Work #31

Open
sorin-ionescu opened this Issue · 19 comments

4 participants

@sorin-ionescu

It claims to not be able to find signature_for_identifier.

It would be helpful if blocks would just work like in MacRuby. I do not wish to distinguish between Nu blocks and Objective-C blocks.

#!/usr/bin/env nush

(load "nu")
(load "cblocks") 

(set myList '("one" "two" "three"))
(set myArray (myList array))

(puts "each: works")
(myList each:
    (do (obj)
        (puts obj)))

(puts "cBlockWithNuBlock: works")
(myArray enumerateObjectsUsingBlock:
    (NuBridgedBlock cBlockWithNuBlock:
        (do (obj idx stop)
            (puts obj))
        signature:"v@I^?")) 

(puts "cblock: fails")
(myArray enumerateObjectsUsingBlock:
    (cblock void ((id) obj (unsigned long) idx (BOOL*) stop)
        (puts obj)))
each: works
one
two
three
cBlockWithNuBlock: works
one
two
three
cblock: fails
Nu uncaught exception: NuCantFindBridgedFunction: dlsym(RTLD_DEFAULT, signature_for_identifier): symbol not found
If you are using a release build, try rebuilding with the KEEP_PRIVATE_EXTERNS variable set.
In Xcode, check the 'Preserve Private External Symbols' checkbox.

  from <TopLevel>:-1: in NuBridgedFunction
  from <TopLevel>:-1: in (NuBridgedFunction functionWithName: "signature_for_identifier" signature: "@@@")
  from <TopLevel>:-1: in ((NuBridgedFunction functionWithName: "signature_for_identifier" signature: "@@@") identifier (NuSymbolTable sharedSymbolTable))
  from <TopLevel>:-1: in g1804289383__get_type_signature
  from <TopLevel>:-1: in set
  from <TopLevel>:-1: in progn
  from <TopLevel>:-1: in bridgedblock
  from <TopLevel>:-1: in (bridgedblock (obj idx stop) (puts obj) ())
  from ./test.nu:23: in cblock
  from ./test.nu:22: in myArray
  from /Library/Frameworks/Nu.framework/Resources/help.nu:136: in progn
@philipwhite

I get a different, more informative error, but it still doesn't work. The signature_for_identifier function only recognizes two types of pointers: id* and void*. So this code does work:

(myArray enumerateObjectsUsingBlock:
   (cblock void ((id) obj (unsigned long) idx (void*) stop)
      (puts obj)))

It should be a pretty simple matter to add support for other pointer types. Other than improving code readability, I don't think it is necessary though. I'll try to work on that this week if no one else if eager to do so.

@sorin-ionescu

Thank you. I hope that in the future, this macro will be unnecessary and that blocks will be bridged automatically like in MacRuby.

@philipwhite
@sorin-ionescu

Objective-C blocks are implemented through NSGlobalBlock, NSStackBlock, and NSMallocBlock. So why not have NuBlock extend the Objective-C block classes? Instead of bridging, you can just downcast and upcast between a NuBlock and an Objective-C block automatically.

@philipwhite
@sorin-ionescu

The Apple blocks documentation does not mention how they are implemented, just the usage, but I have found an article explaining the implementation of Objective-C blocks, and it does not look very complicated. Thus, I do no think that extending it to fit Nu is hairy.

I do not know what MacRuby uses from the Scripting Bridge framework, but I have found that anything that has do with AppleScript and the Scripting Bridge is notoriously slow. I do remember that Objective-C blocks support was added in MacRuby 0.7.

Blocks should just work irrespective of whether they were written in Nu, Objective-C, or MacRuby. I am not aware of the top of my head, but methods that deal with iteration or asynchronicities may return blocks.

@timburks
Owner

Sorin, I think you are right that Nu blocks should be interchangeable with C blocks. I also think that making NuBlock inherit from one of the Objective-C block classes sounds like a good idea. However, I haven't found documentation of these classes, and am concerned that they could be considered private APIs by the iOS app review team. Still, I think it's worth investigating. I am interested to see how MacRuby does this, but haven't looked at its source code yet.

@sorin-ionescu

@timburks clang is open source, is it not? While said classes may not be documented in the usual places, or documented at all, private, I believe, they are not, and even if they were private, they should be safe to use since Apple cannot change them without breaking not only MacRuby but also Objective-C.

Apple should release an App Store self audit tool. One should not have to wait days, weeks, months to only be disappointed by rejection.

@timburks
Owner

Thanks for your thoughts. Of course clang is open source, but as you also know, "Apple should" might not be enough to get what we want in Cupertino.

@ksjogo

Perhaps we could write Apple and ask them?
Blocks seem to be going more important with them and it would be great to be able to use them without hassle.
Some gcd wrappers would be hot as hell too.

@philipwhite
@sorin-ionescu

LuaCocoa is a bridged language. MacRuby and Nu are the only native ones. Let's not use slow bridges.

@philipwhite
@ksjogo

So having something like this?
(do ( (NSArray) array (int) index )
(array objectAtIndex:index))

Looks good for me.

And would it be possible to convert a returned cBlock to a NuBlock when the user provides the signature?

Any news on the iOS side?

@sorin-ionescu

@ksjogo That should be optional. MacRuby doesn't ask you to mention types. It just works.

@ksjogo

That is for sure.
Thinking about one could probably go even further.
Default values and named parameters.
Named parameters would increase the homogeneous interweaving with Objc and provide its readability for blocks.
And default values would decrease the need for boilerplate code.

@philipwhite

Nu amalgamated on my github repository has some updates to the block bridge that should ease the transition to seamless passing of NuBlocks to methods and functions expecting c blocks. I removed the NuBridgedBlock class entirely and moved its functionality to a method -cBlockWithSignature: in the NuBlock class. I also now use the struct definition of a block found in clang documentation when editing the block. Next step will be to make the appropriate changes to set_objc_value_from_nu_value so that when nu_value is a NuBlock and typeString is a block ("@?"), it looks up the signature for the block in bridge support and then calls this new method to get the objc_value.

In regards to iOS, I'm not sure how to fix that problem. I spent several hours yesterday trying to figure this out, but since it works when the debugger is attached, its pretty hard to debug… Given that it dies with signal 9, I kind of suspect that the OS is killing it. Maybe it does some kind of security check and sees that a block is being screwed with. There are quite a few flags in the block object, of which four or five are documented; maybe a flag needs to be set or unset somewhere for it to work. I did some experiments along those lines but gave up for now. Sorry!

@ksjogo

I had an idea to circumvent the block hassling at runtime. I think most times Nu will get shipped with the binary (on iOS a must, on Mac probably because having the user to install a framework is 'not good'). Meaning the developer could customize Nu a bit.
We could add predefined block handlers like the method handlers and then create blocks from the block handlers and let the cblocks just capture our NuBlocks.
For the most common types int/void/float/bool/id return with int/void/float/bool/id we could add the handlers to the Nu.m file, the missing ones the developer could add for himself.

This approach has the disadvantage of being not automatic on uncommen types (we could add them over time) but will be safe to go with upcoming versions because we do no binary tricks with blocks.

What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.