Jump to page: 1 2
Thread overview
Casting to interface not allowed in @safe code?
May 21, 2019
Jim
May 21, 2019
rumbu
May 21, 2019
Jim
May 21, 2019
rumbu
May 21, 2019
Jim
Jun 23, 2019
Nathan S.
Jun 25, 2019
Nathan S.
Jun 25, 2019
Eugene Wissner
May 21, 2019
Daniel Kozak
May 21, 2019
Marco de Wild
May 21, 2019
Jim
May 21, 2019
Hi,

consider this:

interface Base
{
  void setup();
}

interface FeatureX
{
  void x();
}

class Foo: Base, FeatureX
{
  void setup(){};
  void x(){};
}

void main()
{
  Base foo = new Foo(); // This would be the result of a factory class

  (cast(FeatureX)foo).x(); // 1
}

1) error: casting to interface FeatureX is not allowed in @safe code.

Question: How to call foo.x in @safe code ?
May 21, 2019
On Tuesday, 21 May 2019 at 05:51:30 UTC, Jim wrote:
> Hi,
>
> consider this:
>
> interface Base
> {
>   void setup();
> }
>
> interface FeatureX
> {
>   void x();
> }
>
> class Foo: Base, FeatureX
> {
>   void setup(){};
>   void x(){};
> }
>
> void main()
> {
>   Base foo = new Foo(); // This would be the result of a factory class
>
>   (cast(FeatureX)foo).x(); // 1
> }
>
> 1) error: casting to interface FeatureX is not allowed in @safe code.
>
> Question: How to call foo.x in @safe code ?

That's because foo is of type Base, not implementing FeatureX.




May 21, 2019
On Tue, May 21, 2019 at 7:55 AM Jim via Digitalmars-d-learn < digitalmars-d-learn@puremagic.com> wrote:

> Hi,
>
> Question: How to call foo.x in @safe code ?
>
@safe:
interface Base
{
   void setup();
}

interface FeatureX
{
   void x();
}

interface FeatureY
{
   void y();
}


class Foo: Base, FeatureX
{
   void setup(){};
   void x(){};
}

D castHelper(D, S)(S s) @trusted
if ((is(S == interface) || is(S == class)) && (is(D == interface) || is(D
== class)))
{
    import std.traits;
    D d = cast(D)s;
    if (d) return d;
    else assert(0, fullyQualifiedName!S ~ " does not inherit from " ~
fullyQualifiedName!D);
}

void main()
{
   auto foo = new Foo(); // This would be the result of a factory

   castHelper!FeatureX(foo).x;
}


May 21, 2019
On Tuesday, 21 May 2019 at 07:04:27 UTC, rumbu wrote:
> On Tuesday, 21 May 2019 at 05:51:30 UTC, Jim wrote:
>
> That's because foo is of type Base, not implementing FeatureX.

Right, Base isn't implementing FeatureX, but foo is really a Foo which does:

> class Foo: Base, FeatureX
> {
>   void setup(){};
>   void x(){};
> }

and it works in @system code.
May 21, 2019
On Tuesday, 21 May 2019 at 05:51:30 UTC, Jim wrote:
> Hi,
>
> consider this:
>
> interface Base
> {
>   void setup();
> }
>
> interface FeatureX
> {
>   void x();
> }
>
> class Foo: Base, FeatureX
> {
>   void setup(){};
>   void x(){};
> }
>
> void main()
> {
>   Base foo = new Foo(); // This would be the result of a factory class
>
>   (cast(FeatureX)foo).x(); // 1
> }
>
> 1) error: casting to interface FeatureX is not allowed in @safe code.
>
> Question: How to call foo.x in @safe code ?

I got it compiling using `(cast(FeatureX)(cast(Foo)foo)).x();`, but I don't really recommend it. As far as the compiler is concerned, `Base` and `FeatureX` are not related in any way (I'd still expect it to work though). I don't know the circumstances of your problem (so some assumptions here), but usually casting is not the best option. You are basically overriding the type system manually. Some suggestions you can evaluate:
- Extend the base interface:
interface FeatureX : Base { /+...+/}
or
interface Combined : FeatureX, Base {}

- Change the factory class to return either Foo, FeatureX or a templated type (if it's a more general factory class). This way we can leverage the type system.

- You can also make the cast @trusted, but that seems like it kinda defeats the purpose of the function being @safe...

---
Returning to the original point (the cast is disallowed in safe code), I don't think it is listed in the spec: https://dlang.org/spec/function.html#safe-functions Unless I am missing some implementation details about interfaces, I would expect it to work just like class casts (i.e. return null on a failed cast, thereby having defined behaviour, thereby being @safe).

May 21, 2019
On Tuesday, 21 May 2019 at 07:16:49 UTC, Jim wrote:
> On Tuesday, 21 May 2019 at 07:04:27 UTC, rumbu wrote:
>> On Tuesday, 21 May 2019 at 05:51:30 UTC, Jim wrote:
>>
>> That's because foo is of type Base, not implementing FeatureX.
>
> Right, Base isn't implementing FeatureX, but foo is really a Foo

That's your knowledge, for the compiler foo is really a Base, as written in your own code.

The only way to tell the compiler that you safely assume that foo is really a Foo, is to mark code as @trusted, not @safe.

interface Base
{
  void setup();
}

interface FeatureX
{
  @safe void x();
}

class Foo: Base, FeatureX
{
  void setup(){};
  @safe void x(){};
}

@trusted FeatureX imsureitsfeaturex(Base b)
{
  return cast(FeatureX)b;
}

@safe void main()
{
  Base foo = new Foo(); // This would be the result of a factory class
  imsureitsfeaturex(foo).x(); // 1
}
May 21, 2019
On Tuesday, 21 May 2019 at 07:19:38 UTC, Marco de Wild wrote:
> On Tuesday, 21 May 2019 at 05:51:30 UTC, Jim wrote:
>> Hi,
>>
>> consider this:
>>
>> interface Base
>> {
>>   void setup();
>> }
>>
>> interface FeatureX
>> {
>>   void x();
>> }
>>
>> class Foo: Base, FeatureX
>> {
>>   void setup(){};
>>   void x(){};
>> }
>>
>> void main()
>> {
>>   Base foo = new Foo(); // This would be the result of a factory class
>>
>>   (cast(FeatureX)foo).x(); // 1
>> }
>>
>> 1) error: casting to interface FeatureX is not allowed in @safe code.
>>
>> Question: How to call foo.x in @safe code ?
>
> I got it compiling using `(cast(FeatureX)(cast(Foo)foo)).x();`, but I don't really recommend it. As far as the compiler is concerned, `Base` and `FeatureX` are not related in any way (I'd still expect it to work though). I don't know the circumstances of your problem (so some assumptions here), but usually casting is not the best option. You are basically overriding the type system manually. Some suggestions you can evaluate:
> - Extend the base interface:
> interface FeatureX : Base { /+...+/}
> or
> interface Combined : FeatureX, Base {}
>
> - Change the factory class to return either Foo, FeatureX or a templated type (if it's a more general factory class). This way we can leverage the type system.
>
> - You can also make the cast @trusted, but that seems like it kinda defeats the purpose of the function being @safe...
>

The problem I'm trying to solve is I've got to implement a few Backends and some features are present in one Backend and not in another, depending on the OS or the underlying library.

So the idea was to implement the Base interface for each Backend and then additionally the extra features but not those that wouldn't make sense or aren't present in the library.

In the Backend implementation I know that I'm always dealing with an object that implements the Base interface and all the Feature interfaces.

The point of the Base interface is to be able to store those objects into an array.

I could just put all those Features into the Base interface but then for a few Backends I would end up with a lot of functions that would return nothing or throw an exception.

The combined interfaces looks good. Thanks.

> ---
> Returning to the original point (the cast is disallowed in safe code), I don't think it is listed in the spec: https://dlang.org/spec/function.html#safe-functions Unless I am missing some implementation details about interfaces, I would expect it to work just like class casts (i.e. return null on a failed cast, thereby having defined behaviour, thereby being @safe).

That was my expectation as well. But since the 2 interfaces aren't related it kind of makes sense.
May 21, 2019
On Tuesday, 21 May 2019 at 07:33:17 UTC, rumbu wrote:
> On Tuesday, 21 May 2019 at 07:16:49 UTC, Jim wrote:
>> On Tuesday, 21 May 2019 at 07:04:27 UTC, rumbu wrote:
>>> On Tuesday, 21 May 2019 at 05:51:30 UTC, Jim wrote:
>>>
>>> That's because foo is of type Base, not implementing FeatureX.
>>
>> Right, Base isn't implementing FeatureX, but foo is really a Foo
>
> That's your knowledge, for the compiler foo is really a Base, as written in your own code.
>

Yes, thinking about it again it makes sense.
June 23, 2019
On Tuesday, 21 May 2019 at 07:59:13 UTC, Jim wrote:
> On Tuesday, 21 May 2019 at 07:33:17 UTC, rumbu wrote:
>> On Tuesday, 21 May 2019 at 07:16:49 UTC, Jim wrote:
>>> On Tuesday, 21 May 2019 at 07:04:27 UTC, rumbu wrote:
>>>> On Tuesday, 21 May 2019 at 05:51:30 UTC, Jim wrote:
>>>>
>>>> That's because foo is of type Base, not implementing FeatureX.
>>>
>>> Right, Base isn't implementing FeatureX, but foo is really a Foo
>>
>> That's your knowledge, for the compiler foo is really a Base, as written in your own code.
>>
>
> Yes, thinking about it again it makes sense.

It doesn't even slightly make sense. I just ran into this today myself. Unlike Java and C#, casting from Foo to FeatureX is not an assertion that the Foo implements FeatureX. Instead it's how you test at runtime if the class of a specific object derived from Foo implements FeatureX: if it doesn't then the result of the cast is null. I've opened a bug report at https://issues.dlang.org/show_bug.cgi?id=20000.
June 25, 2019
On Sunday, 23 June 2019 at 21:24:14 UTC, Nathan S. wrote:
> https://issues.dlang.org/show_bug.cgi?id=20000.

The fix for this has been accepted and is set for inclusion in DMD 2.080.
« First   ‹ Prev
1 2