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

Reply via email to