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