Thread overview
Declaring interfaces with a constructor
Mar 13, 2017
David Zhang
Mar 13, 2017
XavierAP
Mar 13, 2017
David Zhang
Mar 14, 2017
evilrat
Mar 14, 2017
David Zhang
Mar 15, 2017
evilrat
March 13, 2017
What it says on the tin. Is there a way to create interfaces with a constructor or must I use an abstract class.

Additionally, is there a way to force the linker to link a function in a class without an implementation with another that does have an implementation?

i.e.

---
    //module a;
    class Foo {
        void fun();
    }

    import b: Foo;

    //module b;
    class Foo {
        void fun() { import std.stdio; writeln("!"); }
    }
---

Or something like this? Thanks.
March 13, 2017
On Monday, 13 March 2017 at 02:15:21 UTC, David  Zhang wrote:
> What it says on the tin. Is there a way to create interfaces with a constructor or must I use an abstract class.

What do you want to do in your constructor? I can't think of anything that wouldn't change some state, either of the class (but interfaces aren't allowed to have fields either, precisely because they may not have state), or the global state (worse...). Just curious.

> Additionally, is there a way to force the linker to link a function in a class without an implementation with another that does have an implementation?

I'm not sure if you mean the same as generating "interface files"?
[1] https://dlang.org/dmd-windows.html#interface-files
March 13, 2017
On Monday, 13 March 2017 at 17:52:09 UTC, XavierAP wrote:
> On Monday, 13 March 2017 at 02:15:21 UTC, David  Zhang wrote:
>> What it says on the tin. Is there a way to create interfaces with a constructor or must I use an abstract class.
>
> What do you want to do in your constructor? I can't think of anything that wouldn't change some state, either of the class (but interfaces aren't allowed to have fields either, precisely because they may not have state), or the global state (worse...). Just curious.
>
>> Additionally, is there a way to force the linker to link a function in a class without an implementation with another that does have an implementation?
>
> I'm not sure if you mean the same as generating "interface files"?
> [1] https://dlang.org/dmd-windows.html#interface-files

Basically, I want to define a common interface for a group of platform-specific classes, except that they should ideally also share constructor parameters. What I want to do is then alias them to a common name, selecting the implementation for the target platform at compile time.
March 14, 2017
On Monday, 13 March 2017 at 19:31:52 UTC, David  Zhang wrote:
>
> Basically, I want to define a common interface for a group of platform-specific classes, except that they should ideally also share constructor parameters. What I want to do is then alias them to a common name, selecting the implementation for the target platform at compile time.

like this?
--------------------------------------
import std.stdio;

abstract class PublicInterface
{
    this(int, int) {} // must have body, see below
}

version(broken) {
 alias ActualImpl = NotCompile;
}
else {
 alias ActualImpl = PlatformSpecificClass;
}

// put behind version too, this just for demonstration
class PlatformSpecificClass : PublicInterface
{
    this(int a, int b)
    {
        super(a,b); // yeah, downside of this approach
        writeln(a,b);
    }
}

version(broken) {
 class NotCompile : PublicInterface
 {
  // no interface ctor
 }
}

void main()
{
new ActualImpl(1,2);
}
-----------------------------------------

there is also way to do this using templates and duck typing, I think it will be more idiomatic way since ranges and stuff heavily use it to provide such generalism, though just like you say, I would prefer to have strict interface for such use case...
March 14, 2017
On Tuesday, 14 March 2017 at 02:14:53 UTC, evilrat wrote:
> like this?
> --------------------------------------
> [snip]
> -----------------------------------------
>
> there is also way to do this using templates and duck typing, I think it will be more idiomatic way since ranges and stuff heavily use it to provide such generalism, though just like you say, I would prefer to have strict interface for such use case...

Yeah, that's the idea. Though I just thought of a possibility using an isPublicInterface template. Is that what you meant by templates and duck typing?
March 15, 2017
On Tuesday, 14 March 2017 at 13:54:41 UTC, David  Zhang wrote:
>
> Yeah, that's the idea. Though I just thought of a possibility using an isPublicInterface template. Is that what you meant by templates and duck typing?

Not sure about what that template does, but the idea behind ranges is relying on duck-typing, that means if a provided type(usually a struct in case of ranges) provides some functions with specific signatures we can recognize it is a range. This isn't specifc to just ranges, but a common concept and one of the most widely known use case of it in D is ranges.

Now what i mean is that you just declare interface as contract, implement it for platform specific classes and rely on assumption(yeah, it seems like a bad idea) that your specific realization has specific ctors(well, it won't compile if it doesn't, so no big deal), so you also avoid pitfall of (lack of) multiple inheritance like with base abstract class.

with code shown below as example you also want to do some compile-time checks[1] in your functions when doing work with classes implemented by users in case you are writing a library, so if something is missed users will see a nice error message telling them about what's missing.
-------------------------

interface SomeCommonStuff
{
string getName();
int getRandomNumber();
...
}


version(Windows)
{
alias SomeCommonStuff_Impl = WinSpecifics;
class WinSpecifics : SomeCommonStuff // can also subclass any class
{
  this(int) {...}    // shared interface ctor 1
  this(string) {...} // shared interface ctor 2

  // SomeCommonStuff implementation
  string getName() { return "win"; }
  int getRandomNumber() {return 42; }
  ...
}


version(linux)
{
alias SomeCommonStuff_Impl = LinuxSpecifics;
class LinuxSpecifics : SomeCommonStuff // can also subclass any class
{
  @disable this();   // disallow default ctor on linux
  this(int) {...}    // shared interface ctor 1
  this(string) {...} // shared interface ctor 2

  // SomeCommonStuff implementation
  string getName() { return "linux"; }
  int getRandomNumber() {return 42; }
  ...
}

}

// your fail-safe for checking implementation has your specific ctor, see [1]
// you definitely do want some templates with it for easy checks for interface compliance
static assert(__traits(compiles, new SomeCommonStuff_Impl("string arg")));
static assert(!__traits(compiles, new SomeCommonStuff_Impl("string arg", "second string"))); // this will prevent compiling if you add ctor - this(string, string)

void main()
{
 auto platformClass = SomeCommonStuff_Impl("test");
 auto platformClass1 = SomeCommonStuff_Impl(42);

 // auto platformClass2 = SomeCommonStuff_Impl(); // will not compile on linux because default ctor disabled, but will work anywhere else
}

------------------------

Another similar option is to use so-called Voldemort types, a type that cannot be named. You simply declare private platform specific class in your factory method body to make self-contained unit that returns new instance of that internal class, it can be used as a normal class instance anywhere but cannot be instantiated from outside (since type cannot be named)

Well, D offers quite a lot of flexibility, now i happy to give more insight on what can be done, but i've been out of D for quite a long time.



[1] https://dlang.org/spec/traits.html#compiles