discuss-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Which ObjC2.0 features are missing in the latest GCC?


From: David Chisnall
Subject: Re: Which ObjC2.0 features are missing in the latest GCC?
Date: Mon, 25 Nov 2019 17:46:28 +0000

On 25 Nov 2019, at 14:07, H. Nikolaus Schaller <hns@goldelico.com> 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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]