www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Declaring interfaces with a constructor

reply David Zhang <straivers98 gmail.com> writes:
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.
Mar 12 2017
parent reply XavierAP <n3minis-git yahoo.es> writes:
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
Mar 13 2017
parent reply David Zhang <straivers98 gmail.com> writes:
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.
Mar 13 2017
parent reply evilrat <evilrat666 gmail.com> writes:
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...
Mar 13 2017
parent reply David Zhang <straivers98 gmail.com> writes:
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?
Mar 14 2017
parent evilrat <evilrat666 gmail.com> writes:
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
Mar 14 2017