How’s the poll going? 顺颂商祺 陈北宗 Max Chan, from SushiBits Projects Tel. +86 186-2165-8748 https://github.com/SushiBits
> On Nov 26, 2019, at 17:55, Gregory Casamento <[email protected]> wrote: > > > > 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/
