digitalmars.D.learn - My design need friends
- Namespace (54/54) Oct 05 2013 Hello. I hope one of you has a good idea to solve my design
- Adam D. Ruppe (6/6) Oct 05 2013 It isn't really a fix, but in these situations what I've been
- Namespace (7/13) Oct 05 2013 Yeah, or I let the render method uncommented, so that nobody can
- Namespace (4/20) Oct 05 2013 Something that would expand the package modifier, so that it
- Andrej Mitrovic (2/6) Oct 05 2013 Me too, as have other library writers (like DFL).
- Namespace (64/64) Oct 05 2013 I have now a solution. A bit dirty but it's the D magic I
- Namespace (55/55) Oct 05 2013 Even better as mixin template:
- Andrej Mitrovic (69/70) Oct 05 2013 That's nice.
- Namespace (72/72) Oct 06 2013 This is also nice.
- Andrej Mitrovic (3/4) Oct 06 2013 Let me try to hack on __PRETTY_FUNCTION__ first and I'll post a
Hello. I hope one of you has a good idea to solve my design problem. I have 3 files in two different sub packages. Package Bar has the interface Drawable: ---- module Bar.Drawable; interface Drawable { protected: void _render(); package: final void render() { this._render(); } } ---- and, for example, the class Graphic: ---- module Bar.Graphic; import std.stdio; import Bar.Drawable; class Graphic : Drawable { protected: override void _render() const { writeln("Graphics.render"); } } ---- Package Foo has the class Window: ---- module Foo.Window; import std.stdio; import Bar.Drawable; class Window { public: void draw(Drawable d) { writeln("Window.draw"); d.render(); /// Error: interface Bar.Drawable.Drawable member render is not accessible } } ---- As you can see, I try to access the render method from drawable. But since Drawable is in another sub package, I have no access. That is a situation where I liked to have 'friends' like in C++. I have two solutions: 1. Move Window from package Foo to the package Bar. But that's a ugly solution and I don't want to have any Window components in my Bar package. 2. Make 'render' public instead package. That is my current workaround. But I don't like it much since each other could also access 'render', not only Window. My question is: is there any other possible solutions? Any D magic? Thanks in advance.
Oct 05 2013
It isn't really a fix, but in these situations what I've been doing in my code is just writing: // don't use this /* private */ final public void foo() {} final makes sure it doesn't get overridden wrongly, and then the comments make my intention a little more clear.
Oct 05 2013
On Saturday, 5 October 2013 at 21:46:19 UTC, Adam D. Ruppe wrote:It isn't really a fix, but in these situations what I've been doing in my code is just writing: // don't use this /* private */ final public void foo() {} final makes sure it doesn't get overridden wrongly, and then the comments make my intention a little more clear.Yeah, or I let the render method uncommented, so that nobody can see it in the resulting DDoc. :D But this is no real solution. Maybe we should convince Walter for something like 'friend'? I know that we have something similar with classes in the same module, but that is ugly and unuseable in my situation.
Oct 05 2013
On Saturday, 5 October 2013 at 21:57:58 UTC, Namespace wrote:On Saturday, 5 October 2013 at 21:46:19 UTC, Adam D. Ruppe wrote:Something that would expand the package modifier, so that it belongs to the main package and not to the subpackage, would also fulfill the purpose. Something like 'internal'.It isn't really a fix, but in these situations what I've been doing in my code is just writing: // don't use this /* private */ final public void foo() {} final makes sure it doesn't get overridden wrongly, and then the comments make my intention a little more clear.Yeah, or I let the render method uncommented, so that nobody can see it in the resulting DDoc. :D But this is no real solution. Maybe we should convince Walter for something like 'friend'? I know that we have something similar with classes in the same module, but that is ugly and unuseable in my situation.
Oct 05 2013
On 10/5/13, Adam D. Ruppe <destructionator gmail.com> wrote:It isn't really a fix, but in these situations what I've been doing in my code is just writing: // don't use this /* private */ final public void foo() {}Me too, as have other library writers (like DFL).
Oct 05 2013
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? ----
Oct 05 2013
Even better as mixin template: Accessor: ---- module Core.Accessor; import std.stdio; import std.string : format; import Core.Friend; mixin template Accessor(T) { public: void friendCall(string method, Request, Args...)(ref const Request caller, Args args) { auto friends = __traits(getAttributes, T); static if (friends.length != 0 && is(typeof(friends[0]) == Friend)) { foreach (ref const Friend friend; friends) { if (friend.friend == __traits(identifier, Request)) { mixin("return this." ~ method ~ "(args);"); } } 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))); } } } ---- Drawable: ---- Friend("Window") interface Drawable { protected: void _render(); package: final void render() { this._render(); } public: mixin Accessor!Drawable; } ---- And Window: ---- class Window { public: void draw(Drawable d) { writeln("Window.draw"); //d.render(); /// Error: interface Bar.Drawable.Drawable member render is not accessible d.friendCall!("render")(this); } } ---- Perhaps something should be added to std.typecons.
Oct 05 2013
On 10/6/13, Namespace <rswhite4 googlemail.com> wrote:Any further suggestions or improvements?That's nice. You could also hack away with templates, extracting the full module path (e.g. foo.bar) of the calling module and comparing this with the module a symbol is in, and then do this sort of thing: In a module like foolib.mod: - If some API-marked function is called from any submodule of "foolib" => compilation proceeds - Otherwise, statically assert. However I've just realized we are missing a __MODULE__ equivalent which gives us the full path of the module. For example, for a.b.c returns c, rather than a.b.c. Adam D. Ruppe will probably understand where I'm going with this. Just to pseudocode a bit: test.d: ----- module test; import foo.a; import foo.b; void main() { B b; b.func(); // this will fail } ----- foo/a.d: ----- module foo.a; import foo.b; void test() { B b; b.func(); // ok, compiles } ----- foo/b.d: ----- module foo.b; import foo.internal; struct B { // introduce public function "func" only accessible from any 'foo' submodules // which forwards to "funcImpl" mixin Internal!(funcImpl, "func"); private void funcImpl() { } // internal } ----- foo/internal.d: ----- module foo.internal; mixin template Internal(alias symbol, string funcIdent) { mixin(" public auto " ~ funcIdent ~ "(string packName = __PACKAGE__, T...)(T t) { // note: not the actual assert, you would probably do string comparisons here, // e.g. checking whether both packages start with "foo." static assert(__PACKAGE__ == packName); return symbol(t); } "); } ----- The only issue is there's no way to get to the caller's fully qualified module name. You could hack around extracting this info from a __PRETTY_FUNCTION__ string, but it's unreliable. I think we may need a new __PACKAGE__ symbol.
Oct 05 2013
This is also nice. My final construct now looks like this: Friend: ---- module Core.Friend; struct Friend { public: immutable string FriendClass; immutable string FriendMethod; this(string friendClass, string friendMethod = null) { this.FriendClass = friendClass; this.FriendMethod = friendMethod; } } ---- Accessor: ---- module Core.Accessor; import std.stdio; import std.string : format; import Core.Friend; class FriendException : Exception { public: this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) { super(msg, file, line, next); } } mixin template Accessor(T) { public: void friendCall(string method, Request, Args...)(ref const Request caller, Args args) { auto friends = __traits(getAttributes, T); immutable string ReqStr = __traits(identifier, Request); immutable string Tstr = __traits(identifier, T); static if (friends.length != 0 && is(typeof(friends[0]) == Friend)) { foreach (ref const Friend friend; friends) { if (friend.FriendClass == ReqStr) { if (friend.FriendMethod.length != 0 && method != friend.FriendMethod) { throw new FriendException(format("%s is a friend of %s but no friend of method %s.%s.", ReqStr, Tstr, Tstr, method)); } mixin("return this." ~ method ~ "(args);"); } } throw new FriendException(format("%s is not a friend of %s.", ReqStr, Tstr)); } else { throw new FriendException(format("%s has no friends.", Tstr)); } } } ---- And Drawable: ---- Friend("Window", "render") interface Drawable { protected: void _render(); package: final void render() { this._render(); } public: mixin Accessor!Drawable; } ---- I'm in favor that something like that is really added to std.typecons. And I should write a blog post about your and my solution. :)
Oct 06 2013
On 10/6/13, Namespace <rswhite4 googlemail.com> wrote:And I should write a blog post about your and my solution. :)Let me try to hack on __PRETTY_FUNCTION__ first and I'll post a working example here soon.
Oct 06 2013
On Sunday, 6 October 2013 at 13:11:02 UTC, Andrej Mitrovic wrote:On 10/6/13, Namespace <rswhite4 googlemail.com> wrote:Is there something new?And I should write a blog post about your and my solution. :)Let me try to hack on __PRETTY_FUNCTION__ first and I'll post a working example here soon.
Oct 11 2013
On Friday, 11 October 2013 at 22:39:18 UTC, Namespace wrote:On Sunday, 6 October 2013 at 13:11:02 UTC, Andrej Mitrovic wrote:I have. Foo_1: ---- import std.stdio; import Bar_1; struct friend(T) { public: alias Friend = T; } friend!(Bar) class Foo { public: void call(Bar b) { _call_a_friend(b, this, &b.bar, 42); //b.bar(42); } } ---- Bar_1: ---- import std.stdio; import std.string : format; class Bar { package: void bar(int id) { writeln("Bar.bar with ", id); } } void _call_a_friend(F, T, string filename = __FILE__, size_t line = __LINE__, Args...)(const F friend, const T caller, void delegate(Args) dg, Args args) { bool isFriend = false; foreach (attr; __traits(getAttributes, T)) { isFriend = is(attr.Friend) && is(attr.Friend == F); if (isFriend) break; } //writeln(isFriend); if (isFriend) dg(args); else throw new Exception(format("%s is not a friend of %s.", __traits(identifier, T), __traits(identifier, F)), filename, line); } ---- main: ---- import Foo_1; import Bar_1; void main() { Foo f = new Foo(); Bar b = new Bar(); f.call(b); } ---- I have to admit that the '_call_a_friend' method is a bit parameter heavy. :D Suggestions?On 10/6/13, Namespace <rswhite4 googlemail.com> wrote:Is there something new?And I should write a blog post about your and my solution. :)Let me try to hack on __PRETTY_FUNCTION__ first and I'll post a working example here soon.
Oct 11 2013