Declaring interfaces with a constructor

evilrat via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Mar 14 20:27:40 PDT 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



More information about the Digitalmars-d-learn mailing list