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/
