Re: Compile time data structure
On 09/19/2013 12:53 PM, H. S. Teoh wrote: I hope you're thoroughly confused and utterly perplexed by now No, because you explained it very well. :) readers should instead be directed to std.traits Indeed, I will add some :) of this information to the Traits chapter which comes later in the book. Ali
Re: Compile time data structure
On 09/19/2013 04:32 AM, Dicebot wrote: Some obvious catches: Thank you. I have made those changes except the following one. Variadic template arg chapter should probably mention variadic args of length 1 idiom used to have parameter accepting types, values and aliases at once. Could you please expand on that. Thank you, Ali
Re: User defined attributes use
On Monday, 16 September 2013 at 07:36:13 UTC, simendsjo wrote: I don't have a full example without adding a lot of code, but this partial example might give you the gist of it. // This is the type that validates struct matches(string mustMatch) { alias re = ctRegex!(mustMatch); static string[] validate(T)(const ref T t) { static if(!isSomeString!T) static assert(0, matches only works on strings, not ~T.stringof); return match(t, re).empty ? [no match] : null; } } // and this is the code that runs all validators for a variable void validate(alias T)(ref Appender!(string[]) app) { static if(isTupleWrapper!T) { validate!(T.Head)(app); validate!(T.Tail)(app); } else { foreach(memberAttr; getValidaterAttrs!T) { foreach(attr; memberAttr.Tail) { foreach(msg; attr.validate(T)) if(msg.length) app.put(msg); } } } } // .. And here is some of the plumbing string[] validate(Vars...)() { auto app = appender!(string[])(); validate!Vars(app); return app.data(); } // The getMembersAndAttributesWhere are templates in my little library that isn't released. Uses quite some custom __traits stuff, but it's basically __traits(getAttributes template getValidaterAttrs(alias T) { alias getValidaterAttrs = TypeTuple!(getMembersAndAttributesWhere!(T, isValidationAttr).Elements, getMembersAndAttributesWhere!(TypeOf!T, isValidationAttr).Elements); } // Well.. Incomplete template isValidationAttr(alias T) { enum isValidationAttr = hasMember!(TypeOf!T, validate); } Can I explicitly specify when I can use attribute? Something like this: @attribute(field) struct matches(string mustMatch) { } string wrongAttribute { } class Foo { @matches([0-9]+) string someNumber; //OK, it's a field } @matches([0-9]+) //Error, it's a class, not a field class Bar { } @wrongAttribute //Error, this attribute doesn't exist class C { }
Re: User defined attributes use
On 2013-09-20 08:59, ilya-stromberg wrote: Can I explicitly specify when I can use attribute? Something like this: @attribute(field) struct matches(string mustMatch) { } string wrongAttribute { } class Foo { @matches([0-9]+) string someNumber; //OK, it's a field } @matches([0-9]+) //Error, it's a class, not a field class Bar { } @wrongAttribute //Error, this attribute doesn't exist class C { } Unfortunately you can't. I tag all my structs which are supposed to be used as UDA's with a @attribute UDA. struct attribute {} @attribute struct matches(string mustMatch) {} Then I have some wrappers around __traits(getAttributes) that will, by default, only return UDA's that them self have the @attribute UDA attached to them. https://github.com/jacob-carlborg/orange/blob/master/orange/core/Attribute.d -- /Jacob Carlborg
Re: References
Remove the ref in ref A opAdd(const ref A a) {
References
Running this code I would expect to get ref three times, but ... --- import std.stdio; struct A { int[128] data; ref A opAdd(const ref A a) { A cp = this; cp.data[] += a.data[]; return cp; } } void fun(A a) { writeln(copy); } void fun(const ref A a) { writeln(ref); } void main() { A a, b; fun(a + b); //prints copy fun(A()); //prints copy fun(a); //prints copy } --- After some tests I concluded that is not possible to pass the result of a+b as a reference (which is what interests me most) and this creates an evident performance problem. Is there any solution that I missed ?
Re: References
On Friday, 20 September 2013 at 09:36:18 UTC, andrea9940 wrote: Running this code I would expect to get ref three times, but ... --- import std.stdio; struct A { int[128] data; ref A opAdd(const ref A a) { A cp = this; cp.data[] += a.data[]; return cp; } } void fun(A a) { writeln(copy); } void fun(const ref A a) { writeln(ref); } void main() { A a, b; fun(a + b); //prints copy fun(A()); //prints copy fun(a); //prints copy This prints 'ref' if you change func(A a) to func(const A a) the match of const ref isn't prefered over A a because const need an implicit conversion. } --- After some tests I concluded that is not possible to pass the result of a+b as a reference (which is what interests me most) and this creates an evident performance problem. Is there any solution that I missed ? a + b is an rvalue and will moved to func(A a). This is even faster than ref. ;) A() is also moved. If you have two functions, one with const A a and one with ref const A a, the latter accepts lvalues which are passed by ref and the first accepts rvalues which are moved. So no performance problem. ;) If you're function is a template you can use 'auto ref' which automatically generate two versions of your function: one with and one without ref.
Re: References
On Friday, 20 September 2013 at 09:44:51 UTC, Namespace wrote: This prints 'ref' if you change func(A a) to func(const A a) the match of const ref isn't prefered over A a because const need an implicit conversion. Thanks for the tip. a + b is an rvalue and will moved to func(A a). This is even faster than ref. ;) A() is also moved. Mmm... here is the assembly generated (with -release -O), it seems to me that the struct is always copied. ; fun(a + b) LEA EAX,[EBP-604] PUSH EAX LEA EAX,[EBP-404] PUSH EAX LEA EAX,[EBP-804] CALL 00402010 ;opAdd() MOV EBX,EAX ;pointer to the stack MOV ECX,80 ADD EBX,1FC 004020C3: PUSH DWORD PTR DS:[EBX] ; copy ! SUB EBX,4 ; copy ! LOOP SHORT 004020C3 ; copy ! CALL 00402048 ; fun(const A a) ;fun(A()) PUSH 80 MOV EAX,OFFSET 00422930 PUSH EAX CALL 00405DB0 MOV DWORD PTR SS:[EBP-4],EAX MOV ECX,DWORD PTR SS:[EBP-4] XOR EAX,EAX MOV DWORD PTR DS:[ECX],EAX MOV EAX,DWORD PTR SS:[EBP-4] MOV DWORD PTR DS:[EAX+4],0 ; a lot of movs later ... MOV EAX,DWORD PTR SS:[EBP-4] MOV DWORD PTR DS:[EAX+1FC],0 MOV ESI,DWORD PTR SS:[EBP-4] LEA EDI,[EBP-204] MOV ECX,80 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] ADD ESP,8 ; ^ a lot of istructions to set a struct to zero imo LEA EBX,[EBP-8] MOV ECX,80 0040271B: PUSH DWORD PTR DS:[EBX]; copy ! SUB EBX,4; copy ! LOOP SHORT 0040271B ; copy ! CALL 00402048 ;fun(const A a) ; fun(a) LEA EAX,[EBP-804] ;load the reference CALL 00402060 ; fun(const ref A a)
Re: References
On Friday, 20 September 2013 at 10:29:24 UTC, andrea9940 wrote: On Friday, 20 September 2013 at 09:44:51 UTC, Namespace wrote: This prints 'ref' if you change func(A a) to func(const A a) the match of const ref isn't prefered over A a because const need an implicit conversion. Thanks for the tip. a + b is an rvalue and will moved to func(A a). This is even faster than ref. ;) A() is also moved. Mmm... here is the assembly generated (with -release -O), it seems to me that the struct is always copied. ; fun(a + b) LEA EAX,[EBP-604] PUSH EAX LEA EAX,[EBP-404] PUSH EAX LEA EAX,[EBP-804] CALL 00402010 ;opAdd() MOV EBX,EAX ;pointer to the stack MOV ECX,80 ADD EBX,1FC 004020C3: PUSH DWORD PTR DS:[EBX] ; copy ! SUB EBX,4 ; copy ! LOOP SHORT 004020C3 ; copy ! CALL 00402048 ; fun(const A a) ;fun(A()) PUSH 80 MOV EAX,OFFSET 00422930 PUSH EAX CALL 00405DB0 MOV DWORD PTR SS:[EBP-4],EAX MOV ECX,DWORD PTR SS:[EBP-4] XOR EAX,EAX MOV DWORD PTR DS:[ECX],EAX MOV EAX,DWORD PTR SS:[EBP-4] MOV DWORD PTR DS:[EAX+4],0 ; a lot of movs later ... MOV EAX,DWORD PTR SS:[EBP-4] MOV DWORD PTR DS:[EAX+1FC],0 MOV ESI,DWORD PTR SS:[EBP-4] LEA EDI,[EBP-204] MOV ECX,80 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] ADD ESP,8 ; ^ a lot of istructions to set a struct to zero imo LEA EBX,[EBP-8] MOV ECX,80 0040271B: PUSH DWORD PTR DS:[EBX]; copy ! SUB EBX,4; copy ! LOOP SHORT 0040271B ; copy ! CALL 00402048 ;fun(const A a) ; fun(a) LEA EAX,[EBP-804] ;load the reference CALL 00402060 ; fun(const ref A a) If you have doubt about the assembly, please feeld free to open a bug report. And for performance: import std.stdio; struct A { public: uint id; this(uint id) { this.id = id; writeln(CTor A with , id); } this(this) { writeln(Postblit A with , this.id); } ~this() { writeln(DTor A with , this.id); } A opBinary(string op : +)(ref const A a) { return A(this.id + a.id); } } void func(const A a) { writeln(Value call with A::, a.id); } void func(ref const A a) { writeln(Ref call with A::, a.id); } void main() { A a = A(42); A b = A(23); func(a + b); func(A(1337)); func(a); } Output: CTor A with 42 CTor A with 23 CTor A with 65 Value call with A::65 DTor A with 65 CTor A with 1337 Value call with A::1337 DTor A with 1337 Ref call with A::42 DTor A with 23 DTor A with 42 No Postblit call.
Re: References
Output: CTor A with 42 CTor A with 23 CTor A with 65 Value call with A::65 DTor A with 65 CTor A with 1337 Value call with A::1337 DTor A with 1337 Ref call with A::42 DTor A with 23 DTor A with 42 No Postblit call. The beahvior is correct but at assembly level the compiler still creates a copy on the stack. D code: http://pastebin.com/sTLhnNdV Disassembly: http://pastebin.com/5emmwqJc I'll post a bug report. Thanks for everything.
Re: Compile time data structure
On Friday, 20 September 2013 at 06:20:09 UTC, Ali Çehreli wrote: On 09/19/2013 04:32 AM, Dicebot wrote: Some obvious catches: Thank you. I have made those changes except the following one. Variadic template arg chapter should probably mention variadic args of length 1 idiom used to have parameter accepting types, values and aliases at once. Could you please expand on that. Thank you, Ali Quoting Phobos: ``` template fullyQualifiedName(T...) if (T.length == 1) { static if (is(T[0])) enum fullyQualifiedName = fullyQualifiedNameImplForTypes!(T[0], false, false, false, false); else enum fullyQualifiedName = fullyQualifiedNameImplForSymbols!(T[0]); } ``` It is a relatively common idiom because there is no other way to express a single template parameter that accepts anything.
Re: User defined attributes use
On Friday, 20 September 2013 at 07:57:43 UTC, Jacob Carlborg wrote: On 2013-09-20 08:59, ilya-stromberg wrote: Can I explicitly specify when I can use attribute? Something like this: @attribute(field) struct matches(string mustMatch) { } string wrongAttribute { } class Foo { @matches([0-9]+) string someNumber; //OK, it's a field } @matches([0-9]+) //Error, it's a class, not a field class Bar { } @wrongAttribute //Error, this attribute doesn't exist class C { } Unfortunately you can't. I tag all my structs which are supposed to be used as UDA's with a @attribute UDA. struct attribute {} @attribute struct matches(string mustMatch) {} Then I have some wrappers around __traits(getAttributes) that will, by default, only return UDA's that them self have the @attribute UDA attached to them. https://github.com/jacob-carlborg/orange/blob/master/orange/core/Attribute.d You could of course fix this in a library too. enum AttributeUsage { struct_ = 1 0, class_ = 1 1, //etc } struct attribute { AttributeUsage usage; } Then the library could give a compile-time error if you tries to use it where it's not meant to be.
Re: non virtual interfaces
20.09.2013 12:45, Ali Çehreli пишет: On 09/19/2013 10:31 PM, Alexandr Druzhinin wrote: if I use protected instead of private in interface like: private member functions are non-virtual. But I just use code example from TDPL russian edition. And TDPL says clearly that (un)transmogrify() are private and CardboardBox _maynot_ make (un)transmogrify() non-private - this is highlighted as compiler support of NVI idiom. Is it wrong example, I guess? interface Transmogrifier { final void thereAndBack() { transmogrify(); untransmogrify(); } protected: void transmogrify(); void untransmogrify(); } If they were non-virtual (i.e. private), the calls to transmogrify() and untransmogrify() from thereAndBack() would be bound to Transmogrifier.transmogrify and Transmogrifier.untransmogrify at compile time. That happens and the linker cannot find their definitions. I see. Thanks, I understand it now better class CardboardBox: Transmogrifier { override protected void transmogrify() { } override void untransmogrify() {} } it compiles, but why does compiler permit making untransmogrify() be public? It is up to CardboardBox to decide whether untransmogrify() is public or not. Note that untransmogrify() is still protected when objects are used through the Transmogrifier interface. However, when an object is known to be a CardboardBox so that it is being used through the CardboardBox interface, it is not bound to be a Transmogrifier at that point. Yes, CardboardBox inherits from Transmogrifier but it is CardboardBox's interface that is being used at that point so it decides. Thanks again. So there is no compiler support for NVI idiom? Because if CardboardBox may define its own (un)transmogrify() - TDPL says it possible only if (un)transmogrify() have other signatures. How can I prohibit this? May be it just unrealized yet? You cannot prohibit from Transmogrifier. Ali Unfortunately I tried to use NVI for it namely.
Re: non virtual interfaces
On Friday, September 20, 2013 22:40:48 Alexandr Druzhinin wrote: 20.09.2013 12:45, Ali Çehreli пишет: On 09/19/2013 10:31 PM, Alexandr Druzhinin wrote: if I use protected instead of private in interface like: private member functions are non-virtual. But I just use code example from TDPL russian edition. And TDPL says clearly that (un)transmogrify() are private and CardboardBox _maynot_ make (un)transmogrify() non-private - this is highlighted as compiler support of NVI idiom. Is it wrong example, I guess? TDPL is mostly correct but not completely correct. AFAIK, it has never been implemented that you can override private functions in interfaces like TDPL describes. With classes, package and private are _never_ virtual, so private will have be treated differently interfaces in order to do what TDPL describes. That may or may not be implemented in the future. You can use NVI with classes just fine just so long as you use protected rather than private, but making it private there won't work either, because private is never virtual (and it wouldn't really help you any if it were, because while the base class private function might not be callable, the derived class one would still be callable by the derived class, so trying to prevent the virtual function in NVI from ever being called outside of the base class is broken in the first place - including in C++ where it was originally devised). What NVI helps with is making it so that the public function being called as part of the API is non-virtual, allowing you to do stuff before and after the hidden virtual function being called, but the derived classes can still call their implementation of the hidden, virtual function. - Jonathan M Davis
Re: non virtual interfaces
20.09.2013 23:09, Jonathan M Davis пишет: You can use NVI with classes just fine just so long as you use protected rather than private, but making it private there won't work either, because private is never virtual (and it wouldn't really help you any if it were, because while the base class private function might not be callable, the derived class one would still be callable by the derived class, so trying to prevent the virtual function in NVI from ever being called outside of the base class is broken in the first place - including in C++ where it was originally devised). What NVI helps with is making it so that the public function being called as part of the API is non-virtual, allowing you to do stuff before and after the hidden virtual function being called, but the derived classes can still call their implementation of the hidden, virtual function. - Jonathan M Davis I see. Thanks for clarifying.