Re: "static" means too many things
On 05/02/2012 01:46 AM, bearophile wrote: This is the brief of some D code, it shows one consequence of the excessive overloading of the D "static" keyword: struct Foo { bool solve() { /*static*/ bool fill(int r, int c, Cell n) { // ... if (fill(r + i, c + j, n + 1)) return true; } return fill(x, y, 1); } } The Foo struct has solve() instance method, solve() contains and calls a nested (recursive) function named fill(). fill() has to use instance attributes, so it can't be a static struct function regarding the struct Foo. On the other hand the nested function fill() doesn't need variables defined inside the method solve(), so it doesn't require a frame pointer to solve(), so it's static for solve(). The problem is that "static" is used to denote both nested functions that have no frame pointer (they are often faster and they tend to be less buggy because they can't use names from outer scopes, almost like pure functions), and static struct methods :-) So to denote a function that doesn't need a frame pointer something like "@noframe" sounds better than "static". In practice this is not a very common situation, and fill() here is not performance-critical, so keeping the frame pointer is not so bad (I also annotate fill() with "pure", so despite not being static to solve() it's able to use only immutable names from the scope of solve()). Where performance is very important it suffices to pull fill() out of solve(), define it as a private instance method, and maybe rename it to _fill() or something similar. Bye, bearophile 'fill' should accept the 'this' pointer instead of the frame pointer, without programmer interaction. This is a local optimisation. If you need to guarantee that the double pointer dereference does not occur, make 'fill' a private instance method. I don't see the need of introducing additional syntax here.
std.stdio File doesn't support Win UTF16 file systems?
Hi all, Looking into the source (it uses DM's libc) did I get right that std.stdio.File can't work with UTF16 names in Windows? I've tried to find bug# but failed too. There is Issue#7648 "Can't open file (Windows UTF8)" only but UTF8 is not native for Windows filesystems anyway. Is there any plans to have std.stdio based on std.stream with UTF16 support for filesystems? Thaks, Oleg.
Re: is there a difference between those two notations
On 04/30/2012 11:03 PM, bearophile wrote: Christian Köstlin: reduce!((int a, int b){return a+b;})(iota(100)) reduce!("a+b")(iota(100)) Today the syntaxes I prefer are: iota(100).reduce!q{a + b}() iota(100).reduce!((a, b) => a + b)() But hopefully in some we'll have an efficient sum() function too in Phobos: iota(100).sum() Bye, bearophile thanks for this tip. i always forget about this nice d feature :) regards christian
Access Violation in callback from sort
Just got the TDPL book and it's a great read! I learn best when typing out the code myself, so I decided to make a single VisualD project and put the different exercises in separate modules. I am having problems with sort in std.algorithms - well, actually it appears to be a closure problem when sort calls my delegate. Here's a sample of the problem - in main.d I have //-- module main; import std.algorithm; void main() { uint[string] counts = [ "a":4, "b":5, "c":3, "d":1 ]; string[] keys = counts.keys; sort!((a, b) { return counts[a] > counts[b]; })(keys); } //-- Alone this works just fine. But if I add another file called myalgs.d, and put the following in it: //-- module myalgs; import std.algorithm; //-- Then I get the following exception: object.Error: Access Violation C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm.d(7112): mainmain__T8sortImplS14main9__lambda3VE3std9algorithm12SwapStrategy0TAAyaZsortImpl Steping through the code it appears that the Access Violation occurs when accessing counts[a]. I am using DMD32 v2.059; my compilation command line looks like: dmd -g -debug -X -Xf"$(IntDir)\$(TargetName).json" -deps="$(OutDir)\$(ProjectName).dep" -of"$(OutDir)\$(ProjectName).exe_cv" -map "$(INTDIR)\$(SAFEPROJECTNAME).map" -L/NOMAP Am I doing something wrong here? thanks for the help!
ref semantics with hashes
import std.stdio; struct Foo { int[int] hash; } void main() { test1(); test2(); } void test1() { Foo foo; foo.hash[1] = 1; auto hash2 = foo.hash; hash2[2] = 2; writeln(foo.hash); // [1:1, 2:2] writeln(hash2); // [1:1, 2:2] } void test2() { Foo foo; auto hash2 = foo.hash; hash2[2] = 2; writeln(foo.hash); // [] ?? writeln(hash2); // [2:2] } So if the hash wasn't already initialized then the reference in the Foo struct is a reference to null, and if you duplicate that reference and add a key the old reference still points to null. The only way to ensure a proper link with a hash is to initialize it with a key and then immediately remove that key, which makes the hash not-null but empty: import std.stdio; class Foo { int[int] hash; this() { hash[0] = 0; hash.remove(0); } // make it not null but empty } void main() { test1(); test2(); } void test1() { Foo foo = new Foo; foo.hash[1] = 1; auto hash2 = foo.hash; hash2[2] = 2; writeln(foo.hash); // [1:1, 2:2] writeln(hash2); // [1:1, 2:2] } void test2() { Foo foo = new Foo; auto hash2 = foo.hash; hash2[2] = 2; writeln(foo.hash); // [2:2] writeln(hash2); // [2:2] } Why do we have such error-prone semantics?
Re: Passing array as const slows down code?
On 30/04/12 16:03, Steven Schveighoffer wrote: Try removing the ref and see if it goes back. That usage of ref should not affect anything (if anything it should be slower, since it's an extra level of indirection). Removing the ref ups the time as described before, but only with GDC (DMD the runtime is the same). It's a real effect. There is no implicit local copy for const. I have a suspicion that you changed two things and forgot about one of them when running your tests. Really don't think so. You can even check the version history of changes if you like!
Re: ref semantics with hashes
On Wed, May 02, 2012 at 09:38:35PM +0200, Andrej Mitrovic wrote: [...] > So if the hash wasn't already initialized then the reference in the > Foo struct is a reference to null, and if you duplicate that reference > and add a key the old reference still points to null. > > The only way to ensure a proper link with a hash is to initialize it > with a key and then immediately remove that key, which makes the hash > not-null but empty: [...] > Why do we have such error-prone semantics? I was told that this was expected behaviour. Should the new AA implementation change this, so that it always allocates an empty AA upon instantiation? T -- For every argument for something, there is always an equal and opposite argument against it. Debates don't give answers, only wounded or inflated egos.
Re: cannot cast
Can anyone tell me, why the this code [code] module RefTest.Ref; import std.stdio : writeln; import std.conv : to, toImpl; T const_cast(T : Object)(const T obj) { return cast(T) obj; } struct Ref(T : Object) { private: T _obj; public: @disable this();// { } @disable this(typeof(null));// { } this(T obj) { assert(obj !is null, "Object is null!"); this._obj = obj; } @property inout(T) access() inout { assert(this._obj !is null, "Access: Object is null!"); return this._obj; } //alias access this; // dann kommt "Stackoverflow" oder "recursive expansion" } mixin template TRef(T : Object) { final Ref!(T) getRef(string file = __FILE__, size_t line = __LINE__) in { assert(this !is null, "Object is null! @ " ~ file ~ " in Line " ~ to!(string)(line) ~ "."); } body { return Ref!(T)(this); } final Ref!(const T) getRef(string file = __FILE__, size_t line = __LINE__) const in { assert(this !is null, "Object is null! @ " ~ file ~ " in Line " ~ to!(string)(line) ~ "."); } body { return Ref!(const T)(this); } U opCast(U : Object)() { return *(cast(U*) &this); } alias getRef this; } unittest { bool instanceof(T : Object, U : Object)(const Ref!U obj) { //return const_cast(obj.access).toString() == typeid(T).toString(); const U o = obj.access; return const_cast(o).toString() == typeid(T).toString(); } class A { mixin TRef!(A); } class B : A { } class C : B { } A a1 = new B(); A a2 = new C(); assert(instanceof!(A)(a1) == false); assert(instanceof!(B)(a1)); assert(instanceof!(C)(a1) == false); writeln(a1); B b1 = cast(B) a1; writeln(b1); writeln(); } [/code] fails with: [quote] Fehler 3 instantiated from here: instanceof!(A,A) D:\D\VisualD\Visual Studio 2010\Projects\RefTest\RefTest\Ref.d 76 Fehler 2 Error: template instance RefTest.Ref.const_cast!(A) error instantiating D:\D\VisualD\Visual Studio 2010\Projects\RefTest\RefTest\Ref.d 62 Fehler 4 Error: template instance RefTest.Ref.__unittest1.instanceof!(A,A) error instantiating D:\D\VisualD\Visual Studio 2010\Projects\RefTest\RefTest\Ref.d 76 Fehler 1 Error: function RefTest.Ref.__unittest1.A.TRef!(A).opCast!(A).opCast () is not callable using argument types () D:\D\VisualD\Visual Studio 2010\Projects\RefTest\RefTest\Ref.d 7 [/quote] ? Sounds like a bug.
Transforming a range back to the original type?
Is there a general function for transforming a range back to the original type? If not, would it be possible to create one? -- /Jacob Carlborg
Re: Transforming a range back to the original type?
On 05/02/2012 05:01 PM, Jacob Carlborg wrote: Is there a general function for transforming a range back to the original type? If not, would it be possible to create one? I believe std.array's array function does what you want. -Matt
Re: ref semantics with hashes
On Wednesday, May 02, 2012 13:32:04 H. S. Teoh wrote: > On Wed, May 02, 2012 at 09:38:35PM +0200, Andrej Mitrovic wrote: > [...] > > > So if the hash wasn't already initialized then the reference in the > > Foo struct is a reference to null, and if you duplicate that reference > > and add a key the old reference still points to null. > > > > The only way to ensure a proper link with a hash is to initialize it > > with a key and then immediately remove that key, which makes the hash > > > not-null but empty: > [...] > > > Why do we have such error-prone semantics? > > I was told that this was expected behaviour. > > Should the new AA implementation change this, so that it always > allocates an empty AA upon instantiation? The way that AAs work in this regard is identical to how dynamic arrays work. - Jonathan M Davis
Re: Transforming a range back to the original type?
On Wednesday, May 02, 2012 23:01:21 Jacob Carlborg wrote: > Is there a general function for transforming a range back to the > original type? If not, would it be possible to create one? You mean that if you have something like auto range = getRangeFromSomewhere(); auto newRange = find(filter!func(range), value); you want to transform newRange back to the same type as range? I don't believe that that's possible in the general case. For example, take Array!int a = getArrayFromSomewhere(0; auto range = a[]; auto newRange = find(filter!"a < 7"(range), 2); The type of a[] is specific to Array!int, cannot be created externally from it, and is tightly coupled with the container. What if the container held the values [1, 7, 2, 9, 42, 0, -2, 4]? newRange would end up having [2 , 0, -2 , 4], which doesn't correspond to any range of elements in the Array. So, how could you convert newRange to the same type as range? The best that you could do as far as I can tell would be to create a new Array!int, put those elements in it, and slice the container. So, I don't see how you could really take an arbitrary range and convert it back to its original type. With a very specific set of types, you might be able to (e.g. take([1, 2, 4, 9], 3) could be converted back to int[] easily enough), but with all of the wrapping that goes on with ranges, even determining what type of range is being wrapped by another can't be done generically AFAIK, let alone getting the whole chain down to the original range, let alone somehow converting the wrapped range back to that type. Take remove in std.container, for example. It will only take the container's range type or the result of take on the container's range type - and it had to special case take. And removing a range from a container would be classic example of where you need the original range type rather than a wrapper. - Jonathan M Davis
Re: ref semantics with hashes
H. S. Teoh: I was told that this was expected behaviour. It's trash behavior, bug-prone and not intuitive. It has the worst qualities of both a struct and a class :-) Should the new AA implementation change this, so that it always allocates an empty AA upon instantiation? Sounds like a possibility. Bye, bearophile
Re: Transforming a range back to the original type?
Jacob Carlborg: Is there a general function for transforming a range back to the original type? If not, would it be possible to create one? The newly redesigned containers in Scala language are often able to do this, but this has required the use of a very advanced static type system, that is currently not in D (maybe there are ways to implement it with D templates, but it will require work to implement). Currently there is array.array() that turns the range into a dynamic array. Probably some types/data structures will have a way to turn a lazy range into one of them. Bye, bearophile
Re: ref semantics with hashes
On 5/2/12, Jonathan M Davis wrote: > The way that AAs work in this regard is identical to how dynamic arrays > work. Yes but they're very different in other regards. For example, if you add an element to one array it doesn't automatically add an element to the other array: int[] a = new int[](2); auto b = a; b ~= 1; a ~= 2; // boom, a is now a new array writeln(a); // [0, 0, 2] writeln(b); // [0, 0, 1] This is different from hashes, where adding a new element doesn't magically create a new hash: int[int] ha = [0:0]; auto hb = ha; hb[1] = 1; writeln(ha); // [0:0, 1:1] writeln(hb); // [0:0, 1:1] But maybe this is more relevant to how nulls and references work rather than anything about hashes. In fact what would *actually* solve this problem (for me) is if/when we have aliases implemented for this scenario: import std.stdio; struct Foo { int[] hash; } void main() { Foo foo; alias foo.hash hash2; hash2[0] = 1; writeln(foo.hash); // [1:1] writeln(hash2); // [1:1] } Hopefully we get that one day.
Re: cannot cast
Other, shorter example: [code] import std.stdio, std.traits; class A { int val; alias val this; T opCast(T : Object)() { writeln("FOO"); return to!(T)(this); } } class B : A { } T to(T : Object, U : Object)(const U obj) { return *(cast(T*) &obj); } T const_cast(T)(const T obj) { return cast(T) obj; } void main () { A a = new B(); a.val = 42; writefln("a.val: %d", a.val); B* b = cast(B*) &a; writefln("*b.val: %d", b.val); B b1 = to!(B)(a); writefln("b1.val: %d", b1.val); B b2 = cast(B) a; writefln("b2.val: %d", b2.val); const B b3 = cast(B) a; B b4 = const_cast(b3); } [/code] print: alias_this_impl.d(24): Error: function alias_this_impl.A.opCast!(B).opCast () is not callable using argument types () alias_this_impl.d(44): Error: template instance alias_this_impl.const_cast!(B) e rror instantiating I'm not very skillful in such "template" stories. Maybe someone can help me?
Re: ref semantics with hashes
On 5/3/12, Andrej Mitrovic wrote: > import std.stdio; > > struct Foo > { >int[] hash; > } Sorry that should have been int[int] there.
Re: Passing array as const slows down code?
Joseph Rushton Wakeling: Removing the ref ups the time as described before, but only with GDC (DMD the runtime is the same). It's a real effect. There is no implicit local copy for const. I have a suspicion that you changed two things and forgot about one of them when running your tests. Really don't think so. You can even check the version history of changes if you like! Then it's seems a compiler bug. Please, minimize it as much as possible, and put it in Bugzilla. Bye, bearophile
Test if an enum value is in a range of a derived enum
import std.stdio; enum Base { One, Two, Three, Four } enum Subset : Base { Two = Base.Two, Three = Base.Three } void main() { Base base = Base.Four; if (cast(Subset)base) { writeln("yes"); } } This writes "yes" so cast is a blunt tool that doesn't work in this case. to!() doesn't work either (compile error, maybe I should file this) . My first attempt to implement this check was to use .min/.max of each enum like so: import std.stdio; enum Base { One, Two, Three, Four } enum Subset : Base { Two = Base.Two, Three = Base.Three } void isSubset(Base base) { if (base >= Subset.min && base <= Subset.max) writeln("yes"); else writeln("no"); } void main() { Base base1 = Base.Three; Base base2 = Base.Four; isSubset(base1); // yes isSubset(base2); // no } But this is wrong, because Subset might only define "One" and "Four" in which case "Two" and "Three" will also pass as a subset even though they're not defined in Subset. The alternative is to convert the enum to a string and then to the Subset enum type via the to!() template: import std.stdio; import std.conv; enum Base { One, Two, Three, Four } enum Subset : Base { One = Base.One, Four = Base.Four } void isSubset(Base base) { try { to!(Subset)(to!string(base)); writeln("yes"); } catch (ConvException) { writeln("no"); } } void main() { Base base1 = Base.One; Base base2 = Base.Two; isSubset(base1); // yes isSubset(base2); // no } This works but is arguably slow. Personally I'd love it if I could use the in operator for this, e.g.: Base base1 = Base.Two; assert(base1 in Subset); Anyone know of any other solutions?
Re: Test if an enum value is in a range of a derived enum
Andrej Mitrovic: This writes "yes" so cast is a blunt tool that doesn't work in this case. to!() doesn't work either (compile error, maybe I should file this) . Seems worth filing. Anyone know of any other solutions? Write your own function to perform the test, using a liner loop? Bye, bearophile
extern and opaque structs
I'm doing C bindings and I have an opaque struct and an extern'd variable of the type of that struct. The problem is that dmd is complaining that the struct has no definition (which is true). Making it a pointer works (expected) but i can't do that because the interface is expecting an full struct. Adding __gshared doesn't help. I assume this is bug, since usage of extern means that I don't need to know the size, since it will be allocated in the C code, not the D code. -- James Miller
Re: extern and opaque structs
On Thursday, May 03, 2012 07:00:21 James Miller wrote: > I'm doing C bindings and I have an opaque struct and an extern'd > variable of the type of that struct. The problem is that dmd is > complaining that the struct has no definition (which is true). > Making it a pointer works (expected) but i can't do that because > the interface is expecting an full struct. > > Adding __gshared doesn't help. > > I assume this is bug, since usage of extern means that I don't > need to know the size, since it will be allocated in the C code, > not the D code. If you're putting the struct on the stack, then you need the struct's definition. That's true in C/C++ as well as in D. You can get away without having the definitions to its _functions_, but you need the definition for the struct itself. The only time that you escape this is via pointers. And I don't know how you can talk about the struct as being allocated in the C code if it's on the stack, except perhaps if it has pointers as member variables, in which case it's the memory for those member variables which is allocated in the C code. The entire struct is copied every time that it's passed to anything - unless you're using pointers, which you say you aren't. - Jonathan M Davis