I have now a solution. A bit dirty but it's the D magic I expected.

New file:
----
module Core.Friend;

struct Friend {
public:
        immutable string friend;
}
----

Drawable looks now like this:
----
module Bar.Drawable;

import Core.Friend;

@Friend("Window") interface Drawable {
protected:
        void _render();

package:
        final void render() {
                this._render();
        }
}
----

And window looks like this:
----
class Window {
public:
        void draw(Drawable d) {
                writeln("Window.draw");

//d.render(); /// Error: interface Bar.Drawable.Drawable member render is not accessible
                Accessor.friendCall!(Window, "render")(d);
        }
}
----

As you can see, we have now an 'Accessor':
----
module Bar.Accessor;

import std.stdio;
import std.string : format;

import Core.Friend;

abstract final class Accessor {
public:
static void friendCall(Request, string method, T, Args...)(ref T obj, Args args) {
                auto friend = __traits(getAttributes, T);

static if (friend.length != 0 && is(typeof(friend[0]) == Friend)) {
                        if (friend[0].friend == __traits(identifier, Request)) {
                                mixin("obj." ~ method ~ "(args);");
                        } else {
                                throw new Exception(format("%s is not a friend of 
%s.",
__traits(identifier, Request), __traits(identifier, T)));
                        }
                } else {
                        throw new Exception(format("%s has no friends.",
                                                                           
__traits(identifier, T)));
                }
        }
}
----

Any further suggestions or improvements?
----

Reply via email to