Thread overview
Class info on interfaces
Aug 26, 2015
Jacob Carlborg
Aug 26, 2015
Adam D. Ruppe
Aug 26, 2015
Ali Çehreli
Aug 28, 2015
Jacob Carlborg
Aug 28, 2015
Adam D. Ruppe
Aug 28, 2015
Jacob Carlborg
Aug 28, 2015
rumbu
Aug 28, 2015
Jacob Carlborg
Aug 28, 2015
rumbu
Aug 29, 2015
Jacob Carlborg
August 26, 2015
I noticed the calling "classinfo" on an interface returns the class info of the static type and not the dynamic type. Is that intentional? Perhaps because of COM and C++ interfaces?

module main;

import std.stdio;

interface Foo {}
class Bar : Foo {}

void main()
{
    Foo f = new Bar;
    writeln(f.classinfo);
}

The above program will print the static type "main.Foo" instead of the dynamic type "main.Bar".

-- 
/Jacob Carlborg
August 26, 2015
On Wednesday, 26 August 2015 at 18:53:19 UTC, Jacob Carlborg wrote:
> Is that intentional? Perhaps because of COM and C++ interfaces?

Yes, exactly. COM and C++ things won't necessarily have a D TypeInfo available and since interfaces can be them, it can't be sure.

What I do there is to just cast the interface to Object. Then you should check for null to cover those cases, then you can typeid or classinfo it.
August 26, 2015
On 08/26/2015 11:59 AM, Adam D. Ruppe wrote:
> On Wednesday, 26 August 2015 at 18:53:19 UTC, Jacob Carlborg wrote:
>> Is that intentional? Perhaps because of COM and C++ interfaces?
>
> Yes, exactly. COM and C++ things won't necessarily have a D TypeInfo
> available and since interfaces can be them, it can't be sure.
>
> What I do there is to just cast the interface to Object. Then you should
> check for null to cover those cases, then you can typeid or classinfo it.

To complete, the documentation says ".classinfo applied to an interface gives the information for the interface, not the class it might be an instance of."

  http://dlang.org/property.html#classinfo

Ali

August 28, 2015
On 2015-08-26 20:59, Adam D. Ruppe wrote:

> Yes, exactly. COM and C++ things won't necessarily have a D TypeInfo
> available and since interfaces can be them, it can't be sure.
>
> What I do there is to just cast the interface to Object. Then you should
> check for null to cover those cases, then you can typeid or classinfo it.

Is it possible to detect at compile time if an interface is not a native D interface?

Now when I think about it, we actually have four (!) different kinds of interfaces. Native D, C++, Objective-C and COM.

-- 
/Jacob Carlborg
August 28, 2015
On Friday, 28 August 2015 at 06:19:55 UTC, Jacob Carlborg wrote:
> Is it possible to detect at compile time if an interface is not a native D interface?

Not fully, no, but you might be able to reflect into the methods and see what kind of linkage they have.

http://dlang.org/phobos/std_traits.html#functionLinkage

The interface itself won't necessarily be marked extern - on the binary level, they are all the same (I think... just a pointer to a list of function pointers), but if you look at the methods you can make a reasonably good guess.

However, you can't do anything with an interface that isn't in there anyway without a runtime cast, so you might want to just skip any compile time guesses and just go with the runtime check.

> Now when I think about it, we actually have four (!) different kinds of interfaces. Native D, C++, Objective-C and COM.

aye, and since the interface is so simple at the binary level, it is possible to use them for other things too (I think a glib object in C is also binary compatible...); I'm sure this won't be the end of the list.
August 28, 2015
On Friday, 28 August 2015 at 06:19:55 UTC, Jacob Carlborg wrote:
> On 2015-08-26 20:59, Adam D. Ruppe wrote:
>
>> Yes, exactly. COM and C++ things won't necessarily have a D TypeInfo
>> available and since interfaces can be them, it can't be sure.
>>
>> What I do there is to just cast the interface to Object. Then you should
>> check for null to cover those cases, then you can typeid or classinfo it.
>
> Is it possible to detect at compile time if an interface is not a native D interface?
>
> Now when I think about it, we actually have four (!) different kinds of interfaces. Native D, C++, Objective-C and COM.

I don't know about Objective-C, but:

- for native D interfaces __traits(getVirtualIndex, NativeInterface.firstFunction) == 1 since the first entry in vtbl is the contained object
- for C++ interfaces __traits(getVirtualIndex, CPPInterface.firstFunction) == 0
- COM interfaces: __traits(getVirtualIndex, CPPInterface.firstFunction) == 0 and inherit IUnknown

At runtime, it's simple, you have the m_flags member of TypeInfo_Class (isCPPclass, isCOMclass)

August 28, 2015
On 2015-08-28 16:31, Adam D. Ruppe wrote:

> Not fully, no, but you might be able to reflect into the methods and see
> what kind of linkage they have.
>
> http://dlang.org/phobos/std_traits.html#functionLinkage

That might work.

> However, you can't do anything with an interface that isn't in there
> anyway without a runtime cast, so you might want to just skip any
> compile time guesses and just go with the runtime check.

Well, this would be for my serialization library. If the static type it's not a native D interface I don't have way to recreate the instance when deserializing, at least not with the default deserialization process.

-- 
/Jacob Carlborg
August 28, 2015
On 2015-08-28 17:41, rumbu wrote:

> I don't know about Objective-C, but:
>
> - for native D interfaces __traits(getVirtualIndex,
> NativeInterface.firstFunction) == 1 since the first entry in vtbl is the
> contained object
> - for C++ interfaces __traits(getVirtualIndex,
> CPPInterface.firstFunction) == 0
> - COM interfaces: __traits(getVirtualIndex, CPPInterface.firstFunction)
> == 0 and inherit IUnknown

I'm wondering how reliable that is. Might be better to check the linkage of a method in the interface as Adam suggested.

-- 
/Jacob Carlborg
August 28, 2015
On Friday, 28 August 2015 at 19:36:37 UTC, Jacob Carlborg wrote:
> On 2015-08-28 17:41, rumbu wrote:
>
>> I don't know about Objective-C, but:
>>
>> - for native D interfaces __traits(getVirtualIndex,
>> NativeInterface.firstFunction) == 1 since the first entry in vtbl is the
>> contained object
>> - for C++ interfaces __traits(getVirtualIndex,
>> CPPInterface.firstFunction) == 0
>> - COM interfaces: __traits(getVirtualIndex, CPPInterface.firstFunction)
>> == 0 and inherit IUnknown
>
> I'm wondering how reliable that is. Might be better to check the linkage of a method in the interface as Adam suggested.

The linkage check it's good as long you don't have an abomination like this:

extern(C++) interface CPPInterface
{
    extern(D) void foo();
}

Anyway, the problem is the availability of such function. If the interface doesn't contain any function, there is no way to detect the type, except for COM Interfaces which are clearly implementing IUnknown.

But deriving a new interface and defining a sentinel function, it works:

import std.stdio;
import std.traits;
import core.sys.windows.com;

interface NativeInterface {}

interface COMInterface : IUnknown {}

extern(C++) interface CPPInterface {}

enum InterfaceKind { native, windows, cpp }

template interfaceKind(I) if (is(I == interface))
{
    interface J : I { void __foo__(); }
    static if (functionLinkage!(J.__foo__) == "D")
        enum interfaceKind = InterfaceKind.native;
    else static if (functionLinkage!(J.__foo__) == "Windows")
        enum interfaceKind = InterfaceKind.windows;
    else static if (functionLinkage!(J.__foo__) == "C++")
        enum interfaceKind = InterfaceKind.cpp;
    else static assert(false, "Unknown interface kind.");
}

int main(string[] argv)
{
   static assert(interfaceKind!NativeInterface == InterfaceKind.native);
   static assert(interfaceKind!COMInterface == InterfaceKind.windows);
   static assert(interfaceKind!CPPInterface == InterfaceKind.cpp);
   return 0;
}





August 29, 2015
On 2015-08-28 22:40, rumbu wrote:

> The linkage check it's good as long you don't have an abomination like
> this:
>
> extern(C++) interface CPPInterface
> {
>      extern(D) void foo();
> }

Good point.

> Anyway, the problem is the availability of such function. If the
> interface doesn't contain any function, there is no way to detect the
> type, except for COM Interfaces which are clearly implementing IUnknown.
>
> But deriving a new interface and defining a sentinel function, it works:
>
> import std.stdio;
> import std.traits;
> import core.sys.windows.com;
>
> interface NativeInterface {}
>
> interface COMInterface : IUnknown {}
>
> extern(C++) interface CPPInterface {}
>
> enum InterfaceKind { native, windows, cpp }
>
> template interfaceKind(I) if (is(I == interface))
> {
>      interface J : I { void __foo__(); }
>      static if (functionLinkage!(J.__foo__) == "D")
>          enum interfaceKind = InterfaceKind.native;
>      else static if (functionLinkage!(J.__foo__) == "Windows")
>          enum interfaceKind = InterfaceKind.windows;
>      else static if (functionLinkage!(J.__foo__) == "C++")
>          enum interfaceKind = InterfaceKind.cpp;
>      else static assert(false, "Unknown interface kind.");
> }
>
> int main(string[] argv)
> {
>     static assert(interfaceKind!NativeInterface == InterfaceKind.native);
>     static assert(interfaceKind!COMInterface == InterfaceKind.windows);
>     static assert(interfaceKind!CPPInterface == InterfaceKind.cpp);
>     return 0;
> }

That looks like a pretty good idea, thanks. I'm wondering if it's worth implementing a trait for this in the compiler.

-- 
/Jacob Carlborg