Build interface from abstract class

DigitalDesigns DigitalDesigns at gmail.com
Mon May 28 20:13:49 UTC 2018


On Monday, 28 May 2018 at 11:51:20 UTC, Simen Kjærås wrote:
> On Monday, 28 May 2018 at 09:59:50 UTC, DigitalDesigns wrote:
>> Implementing interfaces can be a pain but are necessary.
>>
>> I like to use abstract classes and provide a base 
>> implementation. It would be cool if I could use D's awesome 
>> meta features to extract the interface from the abstract class 
>> then build it. This requires some funky stuff which I'm not 
>> sure D can do
>
> Creating an interface based on a class is no big deal in D:
>
> interface Interface(T)
> {
>     import std.traits;
>     static foreach (fn; __traits(allMembers, T))
>         static foreach (overload; MemberFunctionsTuple!(T, fn))
>             mixin("ReturnType!overload 
> "~fn~"(Parameters!overload);");
> }
>
> class A
> {
>    void fun() {}
> }
>
> alias IA = Interface!A;
>
> However, you run into a problem when A is expected to implement 
> IA: in order to figure out which functions should be in the 
> interface, we need to know which functions are in A. And in 
> order to figure out which functions are in A, we need to know 
> which functions are in IA. It's a loop with no real solution.
>
> In this specific case, one could consider only members of A, 
> ignoring any interfaces or base classes. This isn't currently 
> possible, but it's not an entirely unreasonable enhancement 
> request.
>
> --
>   Simen

I do not think this is a problem in D. Infinite recursion can 
always be terminated with appropriate means.

1. Use attributes. methods in class A should be marked as being 
for the interface. When added to the interface they will not have 
the attribute so will not be picked up again.

2. Your method of function creation does not pick up attributes 
and other factors so it is weak.


Case 1 should be easy to deal with. Case 2, I am not so sure how 
to build the signature exactly as it is expressed. This is, of 
course, just a copy an paste mechanism but it would require D to 
provide us the signature in some way.

Maybe an easy and direct way would be to pass __FILE__ and 
__LINE__ and extract the line? This seems a bit risky and slow.

Case 1 Will not work if UDA's are required to be exactly the same 
between interface and class. I do not know if D enforces that but 
if it does it seems a bit overkill.


Here is my solution that does not solve problem 2:


import std.stdio;



template InterfaceFromClass(alias T, string id)
{
	auto InterfaceFromClass()
	{
		string s;
		import std.traits;
		foreach (member; __traits(allMembers, T))
		{
			static foreach (overload; MemberFunctionsTuple!(T, member))
			{
				enum attr = __traits(getAttributes, overload);
				static if (__traits(getProtection, __traits(getMember, T, 
member)) == "public")
				{
					mixin(`enum attrs = __traits(getAttributes, T.` ~ member ~ 
`);`);
					foreach(a; attrs)
						static if (is(typeof(a) == string) && a.length > 0 && a == 
"InterfaceMembers")
						{					   					
							s ~= (ReturnType!overload).stringof ~" 
"~member~""~(Parameters!overload).stringof~";";
						}
				}
			}
		}

		return "interface "~id~"\n{\n "~s~"\n}";
	}
}



Which you can see it at work here: 
https://dpaste.dzfl.pl/f49be5dd7daa


More information about the Digitalmars-d-learn mailing list