Hi,

I've (locally, not yet in svn) implemented support for blocks in FPC ( http://en.wikipedia.org/wiki/Blocks_(C_language_extension) ). Blocks are basically procedural variables with support for capturing local context in various ways. As implemented in C/C++/Objective-C, they also introduce the syntactic sugar of declaring them as inline code fragments (anonymous procdures), but I have not implemented this in FPC. Such functionality is completely orthogonal to the support for blocks, and can be added later if/when support for anonymous methods is added to FPC in general.

The current blocks support is very limited and only supports converting regular procedures/functions to blocks (i.e., not nested procedures/functions, not methods of either Pascal or Objective-Pascal classes/objects, not other procedural variables). It is also only enabled on Darwin ((Mac) OS X, iOS and iphonesim) for now, but since the blocks runtime is available on other platforms too, it can be enabled elsewhere over time.

The way I've implemented it right now is that a block-type is declared like a regular procedure variable, followed by "is block". E.g.:

type
NSArrayEnumeratorBlock = procedure (obj: id; idx: NSUInteger; var stop: boolean) is block;

Unlike other procedure variable types, you cannot specify the calling convention of these "is block" types, as it is determined by the blocks runtime. Also unlike other procedure variable types, the compiler will automatically generate wrappers when assigning a procedure/function to such an "is block" type, so that the calling convention of the original procedure/function doesn't matter.

A complete example:

***
{$mode objfpc}
{$modeswitch objectivec2}
{$modeswitch blocks}

uses
  CocoaAll;

type
NSArrayEnumeratorBlock = procedure (obj: id; idx: NSUInteger; var stop: boolean) is block;

  { normally part of the NSArray declaration, but not yet in
    our units since the released compilers don't support blocks
    yet }
  enumcategory = objccategory external (NSArray)
procedure enumerateObjectsUsingBlock(block: NSArrayEnumeratorBlock); message 'enumerateObjectsUsingBlock:';
  end;


procedure myenumerate(obj: id; idx: NSUInteger; var stop: boolean);
begin
NSLog(NSString.alloc.initWithUTF8String('Object at index %lu is %@'), idx, obj);
end;

var
  arr: NSMutableArray;
  str: NSString;
begin
  arr := NSMutableArray.alloc.init;
  str:=NSString.alloc.initWithUTF8String('abc');
  arr.addObject(str);
  str:=NSString.alloc.initWithUTF8String('123');
  arr.addObject(str);
  str:=NSString.alloc.initWithUTF8String('XYZ');
  arr.addObject(str);
  arr.enumerateObjectsUsingBlock(@myenumerate);
end.

Output:

2014-07-16 09:58:53.886 blockenumerate[17438:507] Object at index 0 is abc
2014-07-16 09:58:53.913 blockenumerate[17438:507] Object at index 1 is 123
2014-07-16 09:58:53.914 blockenumerate[17438:507] Object at index 2 is XYZ


Now, to the hard part. In case of global routines, the block does not capture any local state and everything works perfectly as it is. There is however not much point to using them this way, other than with library APIs that accept blocks rather than regular procedure variables (which forms an increasing part of the OS X/iOS system interface, so it is already useful by itself).

"Capturing local state" means that the block gets a copy or a reference to a variable that is local to the context where it is instantiated (instantiated in our case means "the place where we assign the address of a procedure/function to an "is block" variable). For example, when support is added for assigning an instance method to an "is block" variable, the instance pointer must become part of the block because otherwise we don't know what self parameter to use when the block is actually invoked.

However:

* Methods of Pascal classes

As explained above, the "self" parameter has to be captured by the block in this case. If nothing special is done, then this self pointer will be treated like a regular pointer and you will be responsible for freeing it. However, in case a block is passed to an asynchronous routine, the question will most likely be "when is it safe to free that instance", and I'm not sure how to do that. The blocks runtime supports adding code that will be executed when a block is copied and when it is freed, but since Pascal classes don't have an internal reference count, there's not much we can do automatically here.

There is a way to make a Pascal class reference counted, by making it a descendent of TInterfacedObject (or more generally, by implementing the IUnknown interface) and only handling it via interface variables, so maybe the compiler could generate special code in that case to make sure that the block does update the reference counts in that case. That way, you can have a choice between manual and automatic reference counting. I still have to think this through to determine whether this reference counted approach is in fact feasible to implement.

* Methods of Objective-C classes

Objective-C doesn't support function pointers to Objective-C methods (as far as I know), so right now Objective-Pascal doesn't support procedure variables that reference Objective-C methods either. If this is changed because of blocks, they should be supported in general for orthogonality reasons.

This would also require capturing the "self" parameter, but since Objective-C classes to come with reference counting (in various ways), the reference counting would be handled automatically behind the scenes. On the other hand, such a capture could result in cyclic references (https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html#//apple_ref/doc/uid/TP40011210-CH8-SW16 )

* Local/nested procedures

These most closely resemble blocks as they exist in C, since they can directly access all local variables of their enclosing scope (rather than only a specific value such as the self pointer).

The default in C when accessing a local variable from the enclosing scope within a block depends on the type and declaration of the variable: 1) an Objective-C class or a variable marked to behave as such via __attribute__((NSObject)) (e.g. a CFString), or another block-typed variable: reference counts are automatically updated by the compiler/runtime -- except if that variable is declared as __weak, then no reference counting is performed 2) the variable is marked as "__block". In this case, the compiler and runtime use indirection (and if needed, copying onto the heap and updating all redirections) to make sure that the local variable can survive the scope in which it was declared, so that the block (and copies thereof) can keep using it even after the function in which the local variable was declared, returns 3) all other variables (non-block/object and not marked as __block) accessed by a block are simply copied into local storage of the block when the block is instantiated (in our case that would be: when the procedure pointer is assigned to to the "is block" variable). Modifications to such variables in the original routine after the block has been instantiated are not visible to the block and vice verse. If a copy is made of the block, then the current value of those variables within that first block are copied and afterwards again become private to the copy.

I currently have no idea how to encapsulate this in Pascal. We could add __block and __weak modifiers (the __weak modifier would also be useful for non-block-related Objective-Pascal once we add support for ARC/Automated Reference Counting), but that's not really nice.

Another option is to only support by-value copies, and basically always use cases 1) and 3) above depending on the type of the variable. If you want to change something by reference, then you would have to manually set up a pointer (or object instance) first.

Or maybe someone has another idea that combines the flexibility with a Pascalish syntax/way of working?


Jonas
_______________________________________________
fpc-devel maillist  -  [email protected]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply via email to