I'd really like some resolution on this topic.   There seem to be a lot of
reasons for and against.

GC

On Mon, Nov 25, 2019 at 1:04 PM David Chisnall <[email protected]>
wrote:

> On 25 Nov 2019, at 14:07, H. Nikolaus Schaller <[email protected]> wrote:
> > I am not sure that this is the only way to implement it.
> >
> > First of all the callMethodOn returns some block which is a data
> structure knowing that it should take the parameter x and do some function.
> > Let's call it NSBlock. NSBlock can be an ordinary object like any other
> so that it can follow the same memory management rules as used otherwise.
>
> That’s shifting the goalposts somewhat.  It is not news that objects and
> closures are equivalent.  Smalltalk implemented blocks as BlockClosure
> objects, Ian Piumarta’s Composite Object-Lambda Architecture, and C++
> lambdas (which are just shorthand for C++ objects that implement
> `operator()`).  You can always express anything that uses blocks with
> objects.
>
> There are two issues:
>
> 1. If you want to be compatible with existing APIs that use blocks, you
> need to be ABI compatible with blocks.
> 2. The reason that most languages that have objects also have blocks is
> that the shorthand syntax is very convenient.
>
> The following are roughly equivalent:
>
> ```
> @interface Delegate : NSObject
> - (void)invoke;
> - (instancetype)initWithCapture: (id)someObject;
> @end
>
> @implementation Delegate
> {
>         @private
>         id obj;
> }
> - (instancetype)initWithCapture: (id)someObject
> {
>         if ((self = [super init]) == nil) return nil;
>         obj = [someObject retain];
>         return self;
> }
> - (void)invoke
> {
>         [obj doSomething];
> }
> - (void)dealloc
> {
>         [obj release];
>         [super dealloc];
> }
> @end
>
> // At construction site:
>
> [[Delegate alloc] initWithCapture: x];
>
> // At use site:
>
> [delegate invoke];
> ```
>
> And this, with blocks:
>
> ```
> // At construction site:
>
> ^() { [x doSomething]; };
>
> // At use site:
>
> delegate();
> ```
>
> At use, these are similar complexity for the programmer.  At the point of
> construction, one is one line of code (two or three if you put lambda
> bodies on their own lines), the other is 26.  As a programmer, I don’t want
> to write 26 lines of code for a one-line callback.
>
> In C++98 you could probably template that and provide a generic class that
> took a struct containing the captures and a C function, so you’d get a lot
> less boilerplate.  Assuming you had fudged ARC like this (as above, this
> code is typed into a mail client and probably doesn’t compile):
>
> ```
> template<typename T>
> struct ObjCObjectWrapper
> {
>         ObjCObjectWrapper(T x) : obj(objc_retain(x)) {}
>         ObjCObjectWrapper(const ObjCObjectWrapper &other) :
> obj(objc_retain(other.obj) {}
>         ObjCObjectWrapper(ObjCObjectWrapper &&other) : obj(other.obj)
>         {
>                 other.obj = nil;
>         }
>         ObjCObjectWrapper()
>         {
>                 objc_release(obj);
>         }
>         operator=(T x)
>         {
>                 objc_storeStrong(&obj, x);
>         }
>         T operator()
>         {
>                 return obj;
>         }
>         private:
>         T obj;
>
> };
> ```
>
> You could then define a generic capture structure and invoke method like
> this:
>
> ```
> template<typename Capture, typename Ret, typename... Args>
> struct BlockImpl
> {
>         using invoke_t = Ret(*)(Capture &, Args...);
>         void operator()(Args... args)
>         {
>                 inv(capture, std::forward<Args>(args)…);
>         }
>         Block(Capture &&c, invoke_t fn) : capture(c), inv(fn) {}
>         private:
>         Capture capture;
>         invoke_t inv;
> };
> ```
>
> This is then generic and you could use it as follows:
>
> ```
> struct CaptureOneObject
> {
>         ObjCObjectWrapper<id> o;
> };
> void invoke(CaptureOneObject &c)
> {
>         [(id)c.o doSomething];
> }
> // At construction site:
> std::function<void(void)> block(BlockImpl<CaptureOneObject, void>({x},
> invoke));
> // At use site:
> block();
> ```
>
> I *think* you could get the same ABI as blocks if you worked on the
> generic templated boilerplate a bit.
>
> Of course, if you were using C++ then you could also write it using
> lambdas as:
>
> ```
> // At construction site
> ObjCObjectWrapper<id> capture(x);
> auto block = [=capture]() { [(id)capture.o doSomething]; };
> // At use site:
> block();
> ```
>
> And with this you don’t need the invoke function or the capture class.
> Again, much less boiler plate for users, though we don’t have ABI
> compatibility with blocks.
>
> If you were using ARC and C++, then this reduces even further to:
>
> ```
> auto block = [=]() { [x doSomething]; };
> ```
>
> And now we’re back with different syntax for the same thing, though with a
> different ABI (I think Clang has support for implicitly converting C++
> lambdas to blocks, but it’s been a few years since I tried)
>
> David
>
>
>

-- 
Gregory Casamento
GNUstep Lead Developer / OLC, Principal Consultant
http://www.gnustep.org - http://heronsperch.blogspot.com
http://ind.ie/phoenix/

Reply via email to