November 05, 2014
> There is no test for interface-to-class casts in the D/Objective-C test suite, which means you're likely the first person to try that. It's probably just an oversight in the compiler code.

Hey Michel, thanks very much for this explanation! That's actually good news. It certainly will be good to have this fixed, as the target action mechanism gets a lot of it's spice that the "sender" only needs to comply to a protocol and can be whatever control or even nil.

Best! Christian
November 05, 2014
> What happens if you declare "doubleClickAction" like this:
>
> void doubleClickAction(NSTableView sender) { ... }
>
> That will probably require a cast when passing the selector to "setDoubleAction".

Hi Jacob

This just "delegates" the "problem" to another place. The
target/action paradigm in Cocoa programming gets a lot from the
fact that any object can be the sender of an action. In this
particular case I'd even prefer the conditional if the sender
"is" the local table view member var, which then basically has
the same effect as what you suggested above.

I think the best thing will be to fix the interface-to-class
casts in the compiler, as Michel suggests above.

Thanks again
Christian
November 06, 2014
Oh, just found out, it seems that currently the extern C declarations don't work. This comes from the original Chocolat range.d

extern (C) {
nothrow:

	NSRange  	NSUnionRange(NSRange range1, NSRange range2) ;
	NSRange  	NSIntersectionRange(NSRange range1, NSRange range2) ;
	NSString 	NSStringFromRange(NSRange range) ;
	NSRange  	NSRangeFromString(NSString aString) ;

	NSRange 	NSMakeRange(NSUInteger loc, NSUInteger len) ;
	NSUInteger 	NSMaxRange(NSRange range) ;
	bool 		NSEqualRanges(NSRange range1, NSRange range2) ;
}

When trying to use NSMakeRange i get:

Undefined symbols for architecture x86_64:
  "_NSMakeRange", referenced from: ....


Also when I tried to declare / use extern strings like from NSApplication.h:

APPKIT_EXTERN NSString *NSApplicationDidHideNotification;

I found no way to get this working. Is this a limitation of the current 64 bit port?

Thanks again. Christian



November 06, 2014
On 2014-11-06 17:06, Christian Schneider wrote:
> Oh, just found out, it seems that currently the extern C declarations
> don't work. This comes from the original Chocolat range.d
>
> extern (C) {
> nothrow:
>
>      NSRange      NSUnionRange(NSRange range1, NSRange range2) ;
>      NSRange      NSIntersectionRange(NSRange range1, NSRange range2) ;
>      NSString     NSStringFromRange(NSRange range) ;
>      NSRange      NSRangeFromString(NSString aString) ;
>
>      NSRange     NSMakeRange(NSUInteger loc, NSUInteger len) ;
>      NSUInteger     NSMaxRange(NSRange range) ;
>      bool         NSEqualRanges(NSRange range1, NSRange range2) ;
> }
>
> When trying to use NSMakeRange i get:
>
> Undefined symbols for architecture x86_64:
>    "_NSMakeRange", referenced from: ....

I wasn't able to link with the symbol either. But when I tried in Objective-C it worked, but only when I imported Foundation.h, not when I declared NSMakeRange myself. That got me thinking and I had a look in the Foundation NSRange.h header file. "NSMakeRange" and friends are implement directly in the header file to allow inlining. D cannot access inlined functions if they don't exist in a library.

In general you need to reimplement these functions in D. In this particular case, with NSMakeRange, you can just do this in D instead:

auto range = NSRange(1, 2);

The above is a syntax that is allowed for all structs. If you really want to type "NSMakeRange" you need implement the function yourself or make an alias and use the above syntax:

alias NSMakeRange = NSRange;

The downside with the alias is that it allows to use "NSMakeRange" as a struct as well:

NSMakeRange range;

> Also when I tried to declare / use extern strings like from
> NSApplication.h:
>
> APPKIT_EXTERN NSString *NSApplicationDidHideNotification;
>
> I found no way to get this working. Is this a limitation of the current
> 64 bit port?

I think that should work. How did you declare it? It should be declared like this:

extern (C) extern NSString NSApplicationDidHideNotification;

I tried with a standard D compiler and void* instead of NSString and that worked.

"extern (C)" tells the compiler to use C linkage, the second "extern" tells the compiler this symbols is defined somewhere else, i.e. in some library.

-- 
/Jacob Carlborg
November 07, 2014
>> Also when I tried to declare / use extern strings like from
>> NSApplication.h:
>>
>> APPKIT_EXTERN NSString *NSApplicationDidHideNotification;
>>
>> I found no way to get this working. Is this a limitation of the current
>> 64 bit port?
>
> I think that should work. How did you declare it? It should be declared like this:
>
> extern (C) extern NSString NSApplicationDidHideNotification;
>
> I tried with a standard D compiler and void* instead of NSString and that worked.
>
> "extern (C)" tells the compiler to use C linkage, the second "extern" tells the compiler this symbols is defined somewhere else, i.e. in some library.

Jacob, thank you very much for your reply and explanations!

I get EXC_BAD_ACCESS (SIGSEGV) for both NSString and void * if I use the declaration you suggested.

In the case for notification string constants, when I log it in Objective-C, it just equals to "NSApplicationDidHideNotification", so these could be simply redeclared for such strings, but that's not very stylish and against the basic idea, i guess.

November 07, 2014
On 2014-11-07 13:12, Christian Schneider wrote:

> Jacob, thank you very much for your reply and explanations!
>
> I get EXC_BAD_ACCESS (SIGSEGV) for both NSString and void * if I use the
> declaration you suggested.

What exactly are you doing with the string when you get the EXC_BAD_ACCESS? Also, can you reproduce the issue in an program just printing this variable with NSLog?

> In the case for notification string constants, when I log it in
> Objective-C, it just equals to "NSApplicationDidHideNotification", so
> these could be simply redeclared for such strings, but that's not very
> stylish and against the basic idea, i guess.

Yeah, that's not pretty, it should work like in Objective-C.

-- 
/Jacob Carlborg
November 07, 2014
>> I get EXC_BAD_ACCESS (SIGSEGV) for both NSString and void * if I use the
>> declaration you suggested.
>
> What exactly are you doing with the string when you get the EXC_BAD_ACCESS? Also, can you reproduce the issue in an program just printing this variable with NSLog?

I get the SIGSEGV when i try to NSLog this string constant. I was not looking any further, because if it fails to NSLog, i can't do anything with it ;)
November 07, 2014
On 2014-11-07 15:23, Christian Schneider wrote:

> I get the SIGSEGV when i try to NSLog this string constant. I was not
> looking any further, because if it fails to NSLog, i can't do anything
> with it ;)

Now I know what the problem is. In D, module variables are by default in TLS (thread local storage). To make it refer to a global C variable, use __gshared:

extern (C) extern __gshared NSString NSApplicationDidHideNotification;

Sorry, I completely forgot about that.

-- 
/Jacob Carlborg
November 07, 2014
> Now I know what the problem is. In D, module variables are by default in TLS (thread local storage). To make it refer to a global C variable, use __gshared:
>
> extern (C) extern __gshared NSString NSApplicationDidHideNotification;
>
> Sorry, I completely forgot about that.

Ha, awesome! It works! I'll add this to a wiki page in the DiveFramework github repos.

Thanks again!

Oh, and btw, I was briefly looking into the DMD source for trying to fix myself the issue with the protocol to class instance cast (trying to be useful), but I am seriously completely not hardcore enough of digging anything in there, lol. Anyhow, if you got a hint, let me know. It's not just in the example discussed in my other posts, I found it to be an issue in other places as well, as often framework classes return id / ObjcObject instead of a further typed instance. I don't remember where exactly I had a problem, but I remember I used NSObject instead of ObjcObject in these places, which of course is not the way to go.

Have a nice weekend!
November 07, 2014
On 2014-11-07 17:05, Christian Schneider wrote:

> Ha, awesome! It works! I'll add this to a wiki page in the DiveFramework
> github repos.
>
> Thanks again!

No problem :). This isn't mention in the DIP since this has nothing to do with Objective-C, it's rather plain C. This is documented here [1].

> Oh, and btw, I was briefly looking into the DMD source for trying to fix
> myself the issue with the protocol to class instance cast (trying to be
> useful), but I am seriously completely not hardcore enough of digging
> anything in there, lol.

I had no idea what I was doing when I started with this :)

> Anyhow, if you got a hint, let me know. It's not
> just in the example discussed in my other posts, I found it to be an
> issue in other places as well, as often framework classes return id /
> ObjcObject instead of a further typed instance. I don't remember where
> exactly I had a problem, but I remember I used NSObject instead of
> ObjcObject in these places, which of course is not the way to go.

I guess you have to live using NSObject for now, until I fixed that. But in practice NSObject is the only root class. So far I've seen one other class, NSProxy, that doesn't inherit from NSObject.

Ok, I had a quick look at this issue. It is implemented but it's not working. There is a test but that's only casting from a class to an interface, not the other way around. Either there's an issue in the druntime function "_dobjc_interface_cast" or in the compiler (most likely in the compiler).

It looks like the casting is implemented here [2], or at least parts of it.

[1] http://dlang.org/interfaceToC.html
[2] https://github.com/jacob-carlborg/dmd/blob/d-objc/src/e2ir.c#L3843-L3854

-- 
/Jacob Carlborg