> Am 26.11.2019 um 11:09 schrieb Pirmin Braun <[email protected]>: > > I'd suggest a fork, i.e. "Gnustep2" with LLVM, Clang, libobjc2
just came to my mind: ClangSTEP? > > On Tue, 26 Nov 2019 04:55:43 -0500 > 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/ > > > -- > Pirmin Braun [email protected] +49 261 92199400 +49 174 9747584 > Geschäftsführer der Pirmin Braun GmbH www.pirmin-braun.de > Im Palmenstück 4 - 56072 Koblenz - www.facebook.com/PB.ERP.Software > Registergericht: Koblenz HRB 26312 UStID: DE319238613 Steuernummer: > 22/656/03918
