Re: Cannot link using DMD nightly
On Sunday, 28 February 2016 at 22:16:39 UTC, Rene Zwanenburg wrote: On Sunday, 28 February 2016 at 19:02:21 UTC, Matt Elkins wrote: Any suggestions? I don't know how to fix that error, but 2.070.1 has been released and contains a fix for your issue: http://dlang.org/changelog/2.070.1.html ...I don't know how I missed this. Thanks! It works like a champ, including the fix!
Cannot link using DMD nightly
I'm attempting to use the DMD nightly build (because this fix matters a lot to my project: https://issues.dlang.org/show_bug.cgi?id=15661), but the build fails at the link step. Here is my full output: [output] "C:\Program Files (x86)\dub\dub.exe" run --force --build debug --build-mode separate Performing "debug" build using dmd for x86. cake ~master: building configuration "library"... derelict-util 2.0.4: building configuration "library"... derelict-ft 1.0.2: building configuration "library"... derelict-gl3 1.0.18: building configuration "library"... derelict-glfw3 1.1.1: building configuration "library"... gl3n 1.3.1: building configuration "library"... cakegl ~master: building configuration "library"... common ~master: building configuration "library"... client ~master: building configuration "application"... Linking... OPTLINK (R) for Win32 Release 8.00.17 Copyright (C) Digital Mars 1989-2013 All rights reserved. http://www.digitalmars.com/ctg/optlink.html ..\cakegl\out\cakegl.lib(utf) Error 163: Cannot CVPACK Type 0002 --- errorlevel 1 dmd failed with exit code 1. [/output] "cakegl" is one of my libraries used by the application. I have manually removed every trace of any object file or library I could find which was compiled with my previous install of DMD (including the third-party ones pulled by dub, such as derelict), and am also building with the --force flag; I should note that I am using dub for my builds rather than dmd directly. If I swap back in the released version of DMD it all builds fine. This is on 64-bit Windows 7, using the 32-bit DMD nightly build downloaded today, Feb 28th. Any suggestions?
Re: Arrays of noncopyables/Invalid memory operation
On Thursday, 18 February 2016 at 01:14:00 UTC, ZombineDev wrote: https://issues.dlang.org/show_bug.cgi?id=15661. I suggest testing this code again with a newer compiler (see my answer in the other thread - http://forum.dlang.org/post/omfyqfulgyzbrxlzr...@forum.dlang.org). Thanks -- I might get around to that at some point, but for now I'm ok waiting for pre-built binaries unless it becomes more of an issue (I've diverted far off what I need to be working on already). The "Invalid memory operation" error is thrown only by the GC (AFAIK) when the user tries something unsupported like allocating or freeing in a destructor, while a GC collection is being run. I think that it's not possible to trigger it in @nogc code. From a short debug session, I managed to find out that it crashes on the `++foos.length` line, somewhere inside this function: https://github.com/D-Programming-Language/druntime/blob/e47a00bff935c3f079bb567a6ec97663ba384487/src/rt/lifetime.d#L1265. Unfortunately I currently don't have enough time to investigate further. Can you please file a bugzilla issue with this test case? Done: https://issues.dlang.org/show_bug.cgi?id=15705 Thanks for the help!
Re: Arrays of noncopyables/Invalid memory operation
On Friday, 19 February 2016 at 01:30:13 UTC, H. S. Teoh wrote: Suppose the array gets moved sometime after i=500 because it ran out of space in the current memory location. Since there is another slice middleSlice pointing at the old data, it's not just a matter of *moving* the elements over to the new location; they have to be *copied* over so that there are now two copies of the original elements -- the GC doesn't know whether func may try to access the original elements through middleSlice, so it cannot just move them. Only when middleSlice goes out of scope, can the old elements be destructed. So we see that when an array is grown, the elements cannot simply be moved to the new location; we must make copies of them, otherwise any slice of the old data will become invalid. So after an array is moved, there will be two copies of data, and if the original elements are unreferenced after all, the GC will call the dtors when it runs the next collection cycle. Then when the new elements become unreferenced, the dtors will be called again at the following collection cycle, on the new copies of the elements. So it seems that the conclusion is that it is indeed unsafe to store non-copyable objects in an array which might grow (though it would be nice if attempting to do so was a compiler error, like it is with std.container.Array). I've starting creating a custom "Vector" type with value semantics which is move-aware, so hopefully that will address my needs. Thanks for all the help, folks!
Arrays of noncopyables/Invalid memory operation
So in a different thread someone mentioned that when arrays are grown an implicit copy could be called on all the elements, as they might need to be copied over to a new, larger block of memory. This makes sense, and is expected. However, it got me concerned: what if the post-blit was disabled because it is illegal to copy the object? I am using a lot of objects exactly like this, and wanted to make sure my program wasn't working by coincidence since my dynamic arrays are still small for now. So I tested: [code] import std.stdio; @safe: bool scopeEnded; struct Foo { @disable this(this); this(int val) {writeln("Constructing: ", val, " (", , ")"); value = val;} ~this() {writeln("Destroying: ", value, " (", , ")"); assert(value == int.init || scopeEnded);} int value; } unittest { Foo[] foos; for (auto i = 0; i < 1; ++i) { ++foos.length; foos[$ - 1] = Foo(i); } writeln("Scope about to end"); scopeEnded = true; } [/code] [output] Constructing: 0 (18FDA8) Destroying: 0 (18FD6C) Constructing: 1 (18FDA8) Destroying: 0 (18FD6C) Constructing: 2 (18FDA8) Destroying: 0 (18FD6C) Constructing: 410 (18FDA8) Destroying: 0 (18FD6C) Constructing: 411 (18FDA8) Destroying: 0 (18FD6C) Constructing: 412 (18FDA8) Destroying: 0 (18FD6C) Constructing: 413 (18FDA8) Destroying: 0 (18FD6C) Constructing: 414 (18FDA8) Destroying: 0 (18FD6C) Constructing: 415 (18FDA8) Destroying: 0 (18FD6C) core.exception.InvalidMemoryOperationError@src\core\exception.d(679): Invalid memory operation Constructing: 416 (18FDA8) Destroying: 0 (18FD6C) Constructing: 417 (18FDA8) core.exception.InvalidMemoryOperationError@src\core\exception.d(679): Invalid memory operation Destroying: 0 (18FD6C) Constructing: 418 (18FDA8) Destroying: 0 (18FD6C) Constructing: 419 (18FDA8) Destroying: 0 (18FD6C) Constructing: 420 (18FDA8) Destroying: 0 (18FD6C) Constructing: 421 (18FDA8) Destroying: 0 (18FD6C) Constructing: 422 (18FDA8) Destroying: 0 (18FD6C) Constructing: 423 (18FDA8) Constructing: 506 (18FDA8) Destroying: 0 (18FD6C) Constructing: 507 (18FDA8) Destroying: 0 (18FD6C) Constructing: 508 (18FDA8) Destroying: 0 (18FD6C) Constructing: 509 (18FDA8) Destroying: 0 (18FD6C) Destroying: 29 (5201F4) Program exited with code 1 [/output] Note that the invalid memory operation lines change relative order in this output, I think maybe it is stderr instead of stdout. So now I'm wondering: * Is the fact that this compiles a bug? If copying can happen under the hood, shouldn't the @disable this(this) prevent Foo being used this way? Or should copying be happening at all (the compiler could instead choose to "move" the Foo by blitting it and NOT running the destructor...though I don't know whether that is a safe choice in the general case)? * What is the invalid memory operation? It doesn't occur if I remove the assert or make the assert never fail, so I'm guessing it has to do with failing an assert in the destructor? This points bothers me less than the previous one because I don't expect an assert-failing program to behave nicely anyway.
Re: Confusion regarding struct lifecycle
On Wednesday, 17 February 2016 at 07:10:15 UTC, ZombineDev wrote: The downside is that it really indicates that I didn't reduce my buggy program properly. I'll hold out for the live-object-destructor-call fix to see whether that corrects my problem; I can just leak resources until then :). So I've reduced it more properly now, and am 98% sure I've just got another manifestation of that same bug. BTW, you can try the nighly build which should include the bug fix: http://dlang.org/download.html I tried this, and got the same issue. Actually, I was still able to reproduce the original reported issue as well, which leads me to believe either the bug was not actually fixed or (and this is more likely) I screwed something up with my install. Do I need to do anything special to install a nightly build (I'm on Windows)? My "install" procedure was to move my dmd2 folder, verify that compilation now fails since there is no compiler installed, and then copy in the nightly build's dmd2 folder over the original path. Compilation then succeeds (but the bug still manifests). That was running through my IDE (IntelliJ with the D language plugin), and then in turn through DUB. To remove variables, I also tried running dmd directly on the test file: dmd.exe test.d -unittest -main. This compiled but also reproduced the bug. I guess the other possibility is that a patch was made and the issue marked resolved, but the patch hasn't been folded into the nightly build. I have no idea how dmd's patch/build/CI process works, so I don't know whether that is a real possibility.
Re: Confusion regarding struct lifecycle
On Wednesday, 17 February 2016 at 02:23:52 UTC, Ali Çehreli wrote: Since a static array must consist of .init values to begin with, every move into its members must also trigger its destructor if the type has elaborate destructor. Oof. This strikes me as a "gotcha", that this happens even with @disable this() as opposed to a compiler error. Is this only for static arrays, or are there other places @disable this() is silently ignored? This is what I've discovered: Ok, I think that pretty much explains the behavior I was seeing in the reduced case. Thanks -- that's helpful to know! The downside is that it really indicates that I didn't reduce my buggy program properly. I'll hold out for the live-object-destructor-call fix to see whether that corrects my problem; I can just leak resources until then :).
Re: Confusion regarding struct lifecycle
After some more time spent on (the non-reduced version of) this, I think there is a decent chance I am really just experiencing another manifestation of a bug I reported a little bit ago: https://issues.dlang.org/show_bug.cgi?id=15661 The good news is that this is now marked as resolved, so hopefully the next build will have the patch and maybe my problems will go away :).
Re: Confusion regarding struct lifecycle
On Tuesday, 16 February 2016 at 10:45:09 UTC, Marc Schütz wrote: On Tuesday, 16 February 2016 at 04:00:27 UTC, Mike Parker wrote: On Tuesday, 16 February 2016 at 03:39:00 UTC, Matt Elkins wrote: On Tuesday, 16 February 2016 at 03:31:51 UTC, maik klein wrote: In D you can always call Foo.init even with @disable this(), Foo.init can be called implicitly (not just explicitly)? If so, why even have @disable this(), if it offers no guarantees? IMO, this is a bug. It should have to be explicit, just as it is with a single struct instance. There is likely some bug here. In the example, though, the elements _are_ constructed explicitly (except foos[4]). This is legitimate, as the first assignment of an element in a construct counts as construction. The elements are not constructed explicitly with Foo.init; they are constructed explicitly with a user-defined Foo constructor. Since default construction leads to a semantically-invalid object in the non-reduced case, I was expecting that a @disabled default constructor would cause the compiler to complain on attempts to default-construct the struct. Preferably this would be on any attempt to do so, including explicit calls to Foo.init, but at a minimum I would want it to complain on attempts to do so implicitly. Otherwise there don't appear to be any useful guarantees offered by @disable this().
Re: Confusion regarding struct lifecycle
On Tuesday, 16 February 2016 at 08:18:51 UTC, Ali Çehreli wrote: When a temporary Foo object is moved into the array, the temporary object is set to Foo.init. This temporary object lives on the stack. In fact, all temporary Foo objects of Foo.this(int) live at the same location. After Foo(8) is moved into the array and set to Foo.init, now Foo(1) is constructed on top of it. For that to happen, first the destructor is executed for the first life of the temporary, and so on... There is one less Foo.init destruction because conceptually the initial temporary was not constructed on top of an existing Foo.init but raw memory. I guess that makes sense. But doesn't it imply that a copy is happening, despite the @disabled post-blit? The desired behavior was to construct the Foos in place, like with a C++ initializer list.
Re: Confusion regarding struct lifecycle
On Tuesday, 16 February 2016 at 03:31:51 UTC, maik klein wrote: In D you can always call Foo.init even with @disable this(), Foo.init can be called implicitly (not just explicitly)? If so, why even have @disable this(), if it offers no guarantees? The first 3 destructor calls are from the 3 Foo.inits in your static array. But why only 3? There are 5 Foos in the array, and 4 were explicitly overwritten...
Confusion regarding struct lifecycle
I've been bitten again by my lack of understanding of the D struct lifecycle :-/. I managed to reduce my buggy program to the following example: [code] import std.stdio; struct Foo { @disable this(); @disable this(this); this(int valueIn) {value = valueIn;} ~this() {writeln("Foo being destroyed: ", value);} int value; } struct FooList { @disable this(); @disable this(this); this(int) { writeln("Before 8"); foos[0] = Foo(8); writeln("Before 1"); foos[1] = Foo(1); writeln("Before 2"); foos[2] = Foo(2); writeln("Before 3"); foos[3] = Foo(3); writeln("After Foo construction"); } Foo[5] foos; } unittest { auto fooList = FooList(0); writeln("About to lose scope"); } [/code] [output] Before 8 Before 1 Foo being destroyed: 0 Before 2 Foo being destroyed: 0 Before 3 Foo being destroyed: 0 After Foo construction About to lose scope Foo being destroyed: 0 Foo being destroyed: 3 Foo being destroyed: 2 Foo being destroyed: 1 Foo being destroyed: 8 [/output] There are a few things which confuse me about this: * Why does this code compile? In particular, I would have expected that with Foo[5] but initialization for only Foos 0 .. 3 and with the @disabled constructors in Foo, there would be a compiler error. * Where do those first three destroyed Foos come from? I thought there should have been no Foos existing since default construction is @disabled... * Even if somehow the Foos are being created despite the @disabled default constructor, why are only three Foos being destroyed before the scope is lost? So I guess what I'm wondering is: * If I @disable a default constructor on a struct, does the language guarantee that I won't have default-constructed instances of that struct? If not, what is the point of @disable for default constructors? If so, is the above situation a compiler bug or something I am missing? * Is the below the right general syntax for creating an instance of a struct so as to avoid creating more than one copy? If not, what is? Stack variable: auto foo = Foo(5); Member variable: Foo m_foo; this(/* args */) {m_foo = Foo(5);} Thanks!
Re: Things that keep D from evolving?
On Friday, 12 February 2016 at 14:03:05 UTC, Steven Schveighoffer wrote: On 2/10/16 11:51 PM, Matt Elkins wrote: * The in keyword. This is nice syntactic sugar over having a special trait in C++ which deduces whether to pass by value or const-reference. "foo(in bar)" is way more readable than something like "foo(traits::fast_param bar)" Hm... in is short for scope const. It is not pass by reference. Perhaps you meant auto ref? Right...maybe I've been operating under false pretenses, but I was under the impression that the compiler was allowed to interpret scope const as either "pass by value" or "pass by const reference" freely so long as there was no custom post-blit defined? For the purposes of optimization, I mean, to avoid needless copying.
Re: Things that keep D from evolving?
On Friday, 12 February 2016 at 17:20:23 UTC, rsw0x wrote: On Friday, 12 February 2016 at 15:12:19 UTC, Steven Schveighoffer wrote: On 2/12/16 9:37 AM, Matt Elkins wrote: [...] Pass by reference and pass by value means different treatment inside the function itself, so it can't differ from call to call. It could potentially differ based on the type being passed, but I'm unaware of such an optimization, and it definitely isn't triggered specifically by 'in'. 'in' is literally replaced with 'scope const' when it is a storage class. -Steve note that 'in' and 'scope'(other than for delegates) parameter storage class usage should be avoided. It really should be a warning. Why is that?
Re: Things that keep D from evolving?
On Friday, 12 February 2016 at 15:12:19 UTC, Steven Schveighoffer wrote: It could potentially differ based on the type being passed, Yes, that's what I meant. but I'm unaware of such an optimization, Hm. Unfortunate. and it definitely isn't triggered specifically by 'in'. 'in' is literally replaced with 'scope const' when it is a storage class. Yeah, I didn't mean 'in' definitely triggered it. I meant that 'in' (or rather, as you say, 'scope const') provides the conditions by which a compiler could make such an optimization, since it can know that the parameter will be unaffected by the function. It seems like that would mean it could, in theory, choose to pass small objects by value and large objects by reference under the hood, to avoid the large object copy (assuming no custom post-blit...and I guess it would have to check for taking the address?). To achieve that in C++ I use a special trait which deduces whether pass-by-value or pass-by-const-reference makes more sense for the type...but maybe I should be doing the same thing in D, if that optimization isn't actually present? It does seem like the compiler could probably perform that optimization even if 'in' (or 'scope const') wasn't used, if it was smart enough... This sort of micro-optimization generally doesn't matter at the application level unless one has actually profiled it. But it comes up a lot for me when writing generic libraries which can't know whether it will be used in a situation someday where those optimizations do actually matter.
Re: Things that keep D from evolving?
On Tuesday, 9 February 2016 at 13:41:30 UTC, NX wrote: There are several reasons I want to use D rather than C# / Go / something else: I will focus on comparing against C++, because that has been my favorite general purpose language for a long time. While I often have to use C, Java, C#, etc. for various business reasons, when faced with the choice on pure technical merits I will go for C++ any day (haven't tried Go, but was unimpressed by my initial read-over). D is the first language I have ever encountered with a serious chance of unseating C++ as my personal favorite. - Interfacing with native API without jumping through hoops Concur. Though I get this with C++, too. - Incredibly high abstraction and meta-programming possibilities with relatively easier syntax + semantics. Yes. The lack of powerful meta-programming is so frustrating in languages like Java and C#. C++ and D both have the power, but only D has the ease of reading and writing. - It's harder to reverse engineer native code than byte code equivalent. Meh. True, but this doesn't do much for me; it still isn't -that- hard to reverse native code, at least to the point of exploitation (to the point of copying is much harder). It just takes longer. - Trading off anything according to your needs. Yes. This is critical. I actually feel like D does this a little worse than C++ (though not significantly so), if only because it is difficult to completely avoid the GC, and if you want to avoid it and still use inheritance you need to break out the custom allocators. Most of the time this isn't a problem. - Expressiveness and purity, immutablity concepts. Expressiveness is key, though I haven't found D to be terribly more expressive than C++. A little better here, a little worse there. On the other hand, it is usually syntactically nicer when expressing concepts, sometimes greatly so. Immutability is nice. The attention paid to threading was what caused me to take a closer look at D in the first place. - Having GC (but not a horribly slow one) Meh. I know there are things which are much easier to express with a GC, but they don't really come up for me. On the other hand, I often need deterministic cleanup, so the GC can be kind of an annoyance, since it lends itself to a lot of wrapping things in structs and forcing me to pay more attention to lifetime rules than I have to in C++. The other (main?) purported benefits of a GC (avoiding leaks and dangling pointers) don't do much for me, since it is almost trivially easy to avoid those problems in C++ anyway, without introducing the headaches of the GC; certainly it is easier than the focus I have to give D object lifetimes now. That may be a matter of relative practice, though, since I've used C++ for a long long time and D for...3 weeks? :) - Syntactic sugars (associtive arrays, powerful foreach, slices...) I'm still adjusting to the idea of AAs as part of the language rather than library. Not sure I like it, but on the other hand it doesn't really hurt. The foreach construct isn't any better (or worse) than C++'s, unless I'm missing something (which is very possible). But slices are awesome! - Compile times Oh god yes. This makes metaprogramming so much more palatable. - Not bound to a specific platform (unlike C#, easier to do cross-platform work in many cases) I'll go it one step further, and note that D feels more portable than C++ to me...at least to the major platforms I usually work on. Maybe it's the simple fact that things like sockets are defined in the libraries, or that I don't have to #include :). I wish D could be better. I really want it with all of my heart... D has a lot to offer. Here are a few other things I've really liked over C++: * Modules. C++ is supposed(?) to get them at some point I suppose, but for here and now it's a clear advantage for D. * Not syntactically separating interface and implementation (e.g., C++'s header vs source file dichotomy). This was never a true separation in C++, and just led to lots of extra syntax and minor DRY violations. Of course you could write everything inline anyway...until it depended on something declared later. * Related to the point above, not having to think about whether to make something inline. Sure, C++ compilers make that choice for you, but you still have to decide whether to allow them (or at least the ones without link-time code generation) by putting your source in the header file. Needless headache for something a compiler can do. * Properly doing away with the C preprocessor. I haven't seen a need for it that wasn't addressed by another D feature. * Properly doing away with MI. Unlike some languages which just canned it, D actually replaced its functionality with other features. * Thread-local by default. So simple. So useful. * The in keyword. This is nice syntactic sugar over having a special trait in C++ which
Re: Things that keep D from evolving?
On Thursday, 11 February 2016 at 05:05:22 UTC, tsbockman wrote: On Thursday, 11 February 2016 at 04:51:39 UTC, Matt Elkins wrote: - Syntactic sugars (associtive arrays, powerful foreach, slices...) I'm still adjusting to the idea of AAs as part of the language rather than library. Not sure I like it, but on the other hand it doesn't really hurt. The foreach construct isn't any better (or worse) than C++'s, unless I'm missing something (which is very possible). But slices are awesome! In D you can `foreach` over a list of types (AliasSeq) at compile time, not just over ranges at runtime. (For the moment, it's still only available in function bodies though, unlike `static if`.) Neat! I didn't know that. You can do that in C++, but in typical fashion not with a convenient foreach statement. You have to do some crazy type list recursion stuff. So chalk up another point for D's "ease of metaprogramming" :).
Re: Odd Associative Array Reference Behavior
On Thursday, 11 February 2016 at 03:47:09 UTC, Steven Schveighoffer wrote: Misunderstanding. An AA under the hood is simply a pointer. Initialized to null. When you pass it around, you are passing a pointer. AA assign checks for null and allocates a new AA impl to hold the data. But this doesn't affect other copies (that were null). So what is happening is aa() returns a null AA. You assign to it, which allocates a new AA impl, and sets the rvalue to point at it. The rvalue promptly disappears. The original m_aa is still set to point at null. Makes sense (though it defies my intuition; I would have expected an NPE or crash at time of assignment). Thanks!
Odd Associative Array Reference Behavior
Consider the following definition of Foo and an accompanying unittest: [code] struct Foo { @property int[int] aa() {return m_aa;} @property ref int[int] aaRef() {return m_aa;} int[int] m_aa; } unittest { Foo foo; assert(5 !in foo.m_aa); // Sanity-check to start off foo.aa[5] = 1; // Add an element with key 5 assert(5 !in foo.m_aa); // ...huh. 5 didn't make it in? foo.aaRef[5] = 1; // Try again, using the ref variant assert(5 in foo.m_aa); // Works! } [/code] I was under the impression that associative arrays are reference types; if I pass a non-ref "copy" of one, shouldn't insertions still be reflected in the original? Am I dealing with a bug or a misunderstanding on my part?
Re: Odd Destructor Behavior
On Monday, 8 February 2016 at 07:31:07 UTC, Daniel Kozak wrote: Seems to me too, please report it on issues.dlang.org Reported: https://issues.dlang.org/show_bug.cgi?id=15661
Odd Destructor Behavior
I've been experiencing some odd behavior, where it would appear that a struct's destructor is being called before the object's lifetime expires. More likely I am misunderstanding something about the lifetime rules for structs. I haven't been able to reproduce with a particularly minimal example, so I will try to explain with my current code: I have a struct called "TileView", with the relevant parts looking like so: [code] struct TileView { this(Texture.Handle wallsTexture, Texture.Handle topTexture) { // Work happens here, but it doesn't seem to matter to reproducing the condition } // Destructor added for debugging after seeing odd behavior ~this() { import std.stdio; writeln("HERE2"); } // ...more implementation that doesn't seem to affect the condition... } [/code] An instance of this is stored in another struct called "View", with the relevant parts looking like so: [code] struct View { this(/* irrelevant args here */) { writeln("HERE1a"); m_tileView = TileView(Texture.create(loadTGA(makeInputStream!FileInputStream("resources/images/grass-topped-clay.tga").handle)), Texture.create(loadTGA(makeInputStream!FileInputStream("resources/images/grass-outlined.tga").handle)));//, Texture.create(loadTGA(makeInputStream!FileInputStream("resources/images/grass-outlined.tga").handle))); writeln("HERE1b"); } TileView m_tileView; // ...more irrelevant implementation... } [/code] The output from the two writelns in View and the one in TileView is: [output] HERE1a HERE2 HERE1b [/output] So the destructor of TileView is being called during its construction. Flow proceeds normally (e.g., no exception is thrown), as demonstrated by "HERE1b" being printed. Interestingly enough, it all seems to hinge on the second argument to TileView's constructor; if I make it on a separate line beforehand and pass it in, or if I don't pass in a second argument at all, I don't see this behavior. In fact, almost any attempt I've made to reduce the problem for illustration causes it to vanish, which is unfortunate. From this non-reduced situation, does anything jump out? Am I missing something about struct lifetimes? This is the only place I instantiate a TileView. Thanks!
Re: Odd Destructor Behavior
On Sunday, 7 February 2016 at 22:35:57 UTC, anonymous wrote: On 07.02.2016 23:07, Márcio Martins wrote: The destructor you are seeing is from the assignment: m_tileView = TileView(...); This creates a temporary TileView, copies it to m_tileView, and then destroys it. I suppose you want to move it instead. You need to copy the handles from the temporary into the destination, and then clear them out from the temporary to prevent them from being released. I think you're mistaken here. The result of a struct literal is usually moved implicitly. Code: import std.stdio; struct S { ~this() {writeln("dtor");} } void main() { auto s = S(); writeln("end of main"); } Output: end of main dtor If there was a copy that's destroyed after the assignment, there should be another "dtor" before "end of main". Yeah...and I just stuck this into TileView: @disable this(); @disable this(this); and it compiled just fine. If it created a copy I assume the compiler would have choked on that.
Re: Odd Destructor Behavior
On Sunday, 7 February 2016 at 22:04:27 UTC, anonymous wrote: On 07.02.2016 22:49, Matt Elkins wrote: From this non-reduced situation, does anything jump out? Am I missing something about struct lifetimes? This is the only place I instantiate a TileView. Looks weird. I presume this doesn't happen with simpler constructor parameters/arguments, like int instead of Texture.Handle? I don't see how the parameter types would make a destructor call appear. Might be a bug. Correct; if I switch the second Texture.Handle to an int it doesn't happen. Nor if I remove it altogether. Nor if I create the Texture.Handle on the line immediately above TileView's construction, and then pass in the created Texture.Handle. I also didn't understand how the parameters would cause this. Can you post the code for Texture, makeInputStream, etc, so that we have a full, reproducible test case? Oi. Yes, I can, but it is quite a lot of code even if you don't count that it is dependent on OpenGL, GLFW, and gl3n to run to this point. This is why I was disappointed that simpler reproducing cases weren't appearing. I should probably spend more time trying to reduce the case some...
Re: Odd Destructor Behavior
On Sunday, 7 February 2016 at 23:11:34 UTC, anonymous wrote: On 07.02.2016 23:49, Matt Elkins wrote: Oi. Yes, I can, but it is quite a lot of code even if you don't count that it is dependent on OpenGL, GLFW, and gl3n to run to this point. This is why I was disappointed that simpler reproducing cases weren't appearing. I should probably spend more time trying to reduce the case some... Minimal test cases are great, but if you're not able to get it down in size, or not willing to, then a larger test case is ok, too. The problem is clear, and I'd expect reducing it to be relatively straight-foward (but possibly time-consuming). Just don't forget about it completely, that would be bad. Also be aware of DustMite, a tool for automatic reduction: https://github.com/CyberShadow/DustMite Turns out it was less hard to reduce than I thought. Maybe it could be taken down some more, too, but this is reasonably small: [code] import std.stdio; struct TextureHandle { ~this() {} } TextureHandle create() {return TextureHandle();} struct TileView { @disable this(); @disable this(this); this(TextureHandle a, TextureHandle b) {} ~this() {writeln("HERE2");} } struct View { this(int) { writeln("HERE1a"); m_tileView = TileView(create(), create()); writeln("HERE1b"); } private TileView m_tileView; } unittest { auto v = View(5); } [/code] This yields the following: [output] HERE1a HERE2 HERE1b HERE2 [/output] I would have expected only one "HERE2", the last one. Any of a number of changes cause it to behave in the expected way, including (but probably not limited to): * Creating the TextureHandles directly rather than calling create() * Using only one argument to TileView's constructor * Removing TextureHandle's empty destructor That last one especially seems to indicate a bug to me...
Re: Odd Destructor Behavior
Some environment information: DMD 2.070 32-bit Windows 7 (64-bit)
Re: Ownership semantics
On Sunday, 31 January 2016 at 20:10:03 UTC, Matt Elkins wrote: On Sunday, 31 January 2016 at 20:07:26 UTC, Steven Schveighoffer wrote: What is likely happening is that ptr is already collected, and you are invalidly attempting to re-free it. The GC can collect this memory even though there is still an outstanding root-reachable pointer to it? Or maybe it isn't root-reachable?
Re: Ownership semantics
On Sunday, 31 January 2016 at 20:07:26 UTC, Steven Schveighoffer wrote: What is likely happening is that ptr is already collected, and you are invalidly attempting to re-free it. The GC can collect this memory even though there is still an outstanding root-reachable pointer to it?
Re: Ownership semantics
On Sunday, 31 January 2016 at 20:20:52 UTC, Steven Schveighoffer wrote: Oh, nevermind. This is actually simpler. You can't do memory operations inside a destructor during collection. I forgot about that. But the rule I stated is still in force. -Steve So this implies that the UniquePtr implementation originally posted might work with malloc/free calls, or some other non-GC allocator...of course, in that case one could just use std.typecons.Unique.
Declaring rvalue function arguments
I know I can mark an argument ref to require lvalues, so I'm wondering whether there is an equivalent for rvalues; that is, is there a way to specify that an argument to a function MUST be an rvalue? For example, in C++ I can do this: [code] void foo(int && x) {...} foo(5); // Works fine int y = 5; foo(y); // Compile error; y is not an rvalue [/code] This functionality turns out to be really useful when dealing with transferring ownership of resources.
Re: Declaring rvalue function arguments
Errr, ignore the makeFoo() line. Left that in by accident, has no bearing on the issue.
Re: Ownership semantics
On Sunday, 31 January 2016 at 19:34:43 UTC, maik klein wrote: I recently asked a question about ownership semantics in D https://stackoverflow.com/questions/35115702/how-do-i-express-ownership-semantics-in-d But a few minutes ago I found an answer on SO that could potentially explain a lot. http://stackoverflow.com/a/35114945/944430 Sadly it has some pseudo code in it so I implemented it with std.experimental.allocator struct UniquePtr(T) { import std.experimental.allocator; private T* ptr = null; @disable this(this); // This disables both copy construction and opAssign this(Args...)(auto ref Args args){ ptr = theAllocator.make!T(args); } ~this() { theAllocator.dispose(ptr); } inout(T)* get() inout { return ptr; } // Move operations this(UniquePtr!T that) { this.ptr = that.ptr; that.ptr = null; } ref UniquePtr!T opAssign(UniquePtr!T that) { // Notice no "ref" on "that" import std.algorithm.mutation; swap(this.ptr, that.ptr); // We change it anyways, because it's a temporary return this; } } Is this code correct? One problem that I have is UniquePtr!int[int] map; will result in a memory exception and I have no idea why. Interesting. It is something in the dispose, because I changed the destructor to: [code] ~this() { writeln("Disposing ", ptr, " for this ", ); theAllocator.dispose(ptr); writeln("Disposed ", ptr, " for this ", ); } [/code] And I only get the disposing line, not the disposed. I tried taking my ResourceHandle struct that I pasted to you in the other thread earlier and doing the same operation with it (I had to change a const to inout and remove an extraneous 'in' marker to make it compile): [code] alias RH = ResourceHandle!(int*, (int* ptr) {theAllocator.dispose(ptr);}); RH[int] otherMap; otherMap[3] = RH(theAllocator.make!int(5)); [/code] and I get the same invalid memory operation. With either class, I avoid the error if I use a stack member or a static array, but with the associative array or just a dynamic array I get the problem. To see it in a dynamic array: [code] auto map = new UniquePtr!int[1]; map[0] = UniquePtr!int(5); [/code] Not sure what it is...still playing with it.
Re: Declaring rvalue function arguments
On Sunday, 31 January 2016 at 18:02:19 UTC, Matt Elkins wrote: Here is the one I am using right now: Actually, here is the whole module in case you are interested in the unittests/usage: [code] import std.algorithm; import std.traits; struct ResourceHandle(T, alias Deleter, T Default = T.init) { // Constructors/Destructor this(in T handle) {m_handle = handle;} @disable this(this); ~this() {Deleter(m_handle);} // Operators @disable void opAssign(ref ResourceHandle lvalue); ref ResourceHandle opAssign(ResourceHandle rvalue) {swap(m_handle, rvalue.m_handle); return this;} // Methods @property T handle() const {return m_handle;} @property T handle(T handle) {Deleter(m_handle); m_handle = handle; return m_handle;} T release() {T result = m_handle; m_handle = Default; return result;} private: T m_handle = Default; } @nogc @safe nothrow unittest { static uint destroyedCount; static uint lastDestroyed; alias RH = ResourceHandle!(uint, (uint resource){if (resource != uint.init) {lastDestroyed = resource; ++destroyedCount;}}); // Test basic resource cleanup assert(destroyedCount == 0); assert(lastDestroyed != 7); {auto handle0 = RH(7);} assert(destroyedCount == 1); assert(lastDestroyed == 7); // Test releasing { auto handle0 = RH(8); assert(handle0.handle == 8); assert(handle0.release() == 8); assert(handle0.handle == uint.init); assert(destroyedCount == 1); assert(lastDestroyed == 7); } assert(destroyedCount == 1); assert(lastDestroyed == 7); { // Test that copying and lvalue assignment are disabled auto handle0 = RH(5); static assert (!__traits(compiles, {auto handle1 = handle0;})); static assert (!__traits(compiles, {RH handle1; handle1 = handle0;})); // Test that rvalue assignment works auto makeRH(uint value) {return RH(value);} handle0 = makeRH(3); assert(destroyedCount == 2); assert(lastDestroyed == 5); } assert(destroyedCount == 3); assert(lastDestroyed == 3); // Test setting in static array { RH[3] handles; handles[0] = RH(9); assert(destroyedCount == 3); assert(lastDestroyed == 3); } assert(destroyedCount == 4); assert(lastDestroyed == 9); // Test setting to resource directly { auto handle0 = RH(11); assert(destroyedCount == 4); assert(lastDestroyed == 9); assert(handle0.handle == 11); handle0.handle = 12; assert(destroyedCount == 5); assert(lastDestroyed == 11); assert(handle0.handle == 12); } assert(destroyedCount == 6); assert(lastDestroyed == 12); } [/code]
Re: Ownership semantics
On Sunday, 31 January 2016 at 20:11:07 UTC, Matt Elkins wrote: On Sunday, 31 January 2016 at 20:10:03 UTC, Matt Elkins wrote: On Sunday, 31 January 2016 at 20:07:26 UTC, Steven Schveighoffer wrote: What is likely happening is that ptr is already collected, and you are invalidly attempting to re-free it. The GC can collect this memory even though there is still an outstanding root-reachable pointer to it? Or maybe it isn't root-reachable? No, it is still present even if root-reachable: [code] unittest { import std.algorithm; int* x; { auto map = new UniquePtr!int[1]; auto uniqueX = UniquePtr!int(5); x = uniqueX.get(); map[0] = move(uniqueX); } } [/code] [output] core.exception.InvalidMemoryOperationError@src\core\exception.d(679): Invalid memory operation Program exited with code 1 Made 632560 for this 18FD90 Disposing null for this 18FD70 Disposed null for this 18FD70 Disposing null for this 18FD90 Disposed null for this 18FD90 All unit tests have been run successfully. Disposing 632560 for this 632550 [/output]
Re: Declaring rvalue function arguments
On Sunday, 31 January 2016 at 17:42:19 UTC, anonymous wrote: I don't know if this works in all cases, but it passes that simple test: @disable void foo(ref int x); void foo(int x) {} void main() { foo(5); /* works */ int y = 5; foo(y); /* error */ } My fault, I should have better explained the situation I'm running into. I've boiled it down to this: [code] struct Foo { @disable this(this); @disable void opAssign(ref Foo); void opAssign(Foo foo) {} } unittest { void bar(Foo foo) { Foo foo1; foo1 = foo; // Fails to compile here } Foo makeFoo() {return Foo();} bar(Foo()); } [/code] [output] Error: function Foo.opAssign is not callable because it is annotated with @disable [/output] Note that if I don't declare and assign foo1 on separate steps it yells at me for the post-blit constructor being disabled, which is reasonable. But it seems like the rvalue assignment operator should work...
Re: Declaring rvalue function arguments
On Sunday, 31 January 2016 at 17:48:53 UTC, maik klein wrote: The problem is that x will be copied afaik which is not what you want if you want to deal with ownership. I think that can be solved by wrapping the resource in a struct that deals with passing the ownership. Here is the one I am using right now: [code] struct ResourceHandle(T, alias Deleter, T Default = T.init) { // Constructors/Destructor this(in T handle) {m_handle = handle;} @disable this(this); ~this() {Deleter(m_handle);} // Operators @disable void opAssign(ref ResourceHandle lvalue); ref ResourceHandle opAssign(ResourceHandle rvalue) {swap(m_handle, rvalue.m_handle); return this;} // Methods @property T handle() const {return m_handle;} @property T handle(T handle) {Deleter(m_handle); m_handle = handle; return m_handle;} T release() {T result = m_handle; m_handle = Default; return result;} private: T m_handle = Default; } [/code]
Re: Declaring rvalue function arguments
On Sunday, 31 January 2016 at 17:55:53 UTC, Matt Elkins wrote: Errr, ignore the makeFoo() line. Left that in by accident, has no bearing on the issue. Ok, I think I understand why this doesn't work, at least. The Foo passed into bar() is, of course, an lvalue itself. So I can achieve this with a new bar(), like so: [code] void bar(Foo foo) { import std.algorithm.mutation; Foo foo1 = move(foo); } [/code]
Re: Access Violation in @safe Code
On Saturday, 30 January 2016 at 13:37:43 UTC, Kagamin wrote: Alias templates require stack pointer, init probably has it set to null. Try this: FooType foo = FooType(); Yes, that fixed it. Interesting.
Re: Relocatable objects and internal pointers
On Saturday, 30 January 2016 at 01:28:54 UTC, H. S. Teoh wrote: On Sat, Jan 30, 2016 at 01:21:27AM +, Matt Elkins via Digitalmars-d-learn wrote: On Saturday, 30 January 2016 at 01:18:33 UTC, Ali Çehreli wrote: >Definitely so. Rvalues are moved around all the time. The >following program has two rvalue moves without calling >post-blits or destructors. Oi, that makes life tough. Ok, I'll figure something else out, then... [...] Keep in mind that D structs are conceptually different from C++ structs (even if they are similarly implemented). D structs are supposed to be value types with POD-like semantics; so when passing structs around they are bit-copied into the destination and then the postblit method (this(this)) is called to "patch up" the copy. This is unlike in C++ where you have copy ctors and dtors and operator=() to manage copying. Because there are no copy ctors, having internal pointers can be dangerous, since structs can move around in memory without any warning (e.g., returning a struct from a function generally involves copying it from the callee's stack frame into a local variable in the caller's stack frame). If you need something with internal pointers, you might want to consider classes instead. Either that, or be sure to allocate your structs on the heap instead, and work with pointers instead of the struct values directly. (Note that this is still risky, since somebody might dereference the pointer and get a stack copy of the struct, which will cause problems when it then gets passed around.) T Yeah, but the whole point of what I am doing is to avoid using the heap; I can think of several ways to implement this if I relax that restriction :). I'm basically trying to make C++'s std::unique_ptr for resource handles, a thin wrapper which ensures resource cleanup and allows moving the handle. Since I'm putting it in my lowest-level/most-generic library with no visibility on how it gets used, I want it very lightweight (ideally zero-cost, like I can do in C++11, or at least low-cost [sans heap] like I could do in C++98) so that I can use it with the broadest range of higher-level applications.
Relocatable objects and internal pointers
Hi all, I'm a C++ programmer trying to decide whether to switch my main focus to D, and so I'm working on a pet project using it. So far I really like some of the practical aspects of the language (built-in contracts are great, the metaprogramming is very accessible, and I can't enough of these compile speeds!), but I keep finding myself frustrated by what seem like expressiveness limitations (unless, as I hope, they are just examples of my newbieness shining through). Case in point: In an attempt to work around one apparent limitation (previously asked about here http://forum.dlang.org/thread/eizmagtimvetogana...@forum.dlang.org) I came up with an idea which would require storing internal points in a struct. A very stripped-down but illustrative example would be something like this: [code] struct Foo { invariant { assert(m_this == ); } @disable(this); this(/* arguments to populate stuff */) { m_this = /* ... populate stuff ... */ } this(this) { m_this = /* ... do more stuff ... */ } private: Foo* m_this; /* ... stuff ... */ } [/code] This is just a piece of what I am doing, if you are wondering why I am bothering to save a pointer to this. However, I was doing some reading on D and came across a section in TDPL which said internal pointers are verboten because objects must be relocatable. Does this mean my example is invalid (e.g., the invariant will not hold in all circumstances)? If it is invalid, does that mean there are circumstances under which the post-blit constructor can be elided when performing a copy or copy-like operation (such as a move)? I've been treating it like a sort of copy-constructor that lacks visibility on the copied-from object, but maybe that's a mistake...
Re: Access Violation in @safe Code
On Saturday, 30 January 2016 at 05:18:08 UTC, Steven Schveighoffer wrote: https://issues.dlang.org/enter_bug.cgi -Steve Added! https://issues.dlang.org/show_bug.cgi?id=15627 Thanks for the help.
Re: Relocatable objects and internal pointers
On Saturday, 30 January 2016 at 02:09:55 UTC, Steven Schveighoffer wrote: I figured out a way to have them. You just have to guarantee you don't copy the actual "pointer" out of the struct: https://forum.dlang.org/post/mk5k4l$s5r$1...@digitalmars.com Unfortunately, that won't work for what I was trying to do. The stuff I elided in the comments were more pointers to other Foo instances, used to create a linked-list (of stack-allocated objects); these would still break under the conditions Ali described. I was only storing the this pointer so that blitted objects could deduce where they came from (trying to turn the post-blit constructor into a copy-constructor). Thanks, though. I'm thinking that maybe D just can't express these semantics without substantial overhead. While somewhat disappointing (I came into D with stars in my eyes :)), it's not enough by itself to make me go back to C++, at least not just yet. Not when I can just use a few static ifs to do what previously required careful template crafting that I wouldn't understand 3 months later. On the other hand, I'm falling behind on my library books since I no longer have any time for reading during compilations ;).
Re: Relocatable objects and internal pointers
On Saturday, 30 January 2016 at 03:00:11 UTC, Steven Schveighoffer wrote: There are some really smart people who frequent these forums, if you post your actual use case, you may get an answer that you hadn't thought of. Yeah, I tried that first (on the general forum, since at the time I didn't know about this one). I saw you were trying to implement something like std::unique_ptr? There is http://dlang.org/phobos/std_typecons.html#.Unique std.typecons.Unique seems to require heap allocation, which makes it a far cry from std::unique_ptr.
Conditional nothrow/safe/nogc/etc?
Is there any way to specify that a generic function is conditionally nothrow (or other function decorators), based on whether the template instantiation is nothrow? I'm looking for something akin to C++'s noexcept(noexcept()), e.g.: template void foo() noexcept(noexcept(T())) {} I don't see anything about it on the functions grammar page (https://dlang.org/spec/function.html). I do see something to do with nothrow_ on the std.traits page, but I'm not sure how to use it to achieve this or whether that is the right approach. My actual use case is a non-generic method opAssign inside of a generic struct.
Access Violation in @safe Code
Title says it; I get an access violation in code marked @safe. Here's a minimal example: [code] @safe: struct Foo(alias Callback) { ~this() {Callback();} } unittest { uint stackVar; alias FooType = Foo!((){++stackVar;}); FooType[1] foos; foos[0] = FooType.init; } [/code] This results in: object.Error@(0): Access Violation 0x00405E2A in pure nothrow @nogc @safe void test.__unittestL9_4().__lambda1() at \test.d(12) ... more stack ... Line 12 is the alias FooType line, where the delegate is defined. Where is this coming from? Intuition says it is something to do with calling the delegate after the stack frame has popped and stackVar is unreachable, but I'm not seeing it. Wouldn't foos be destructed before the stack frame is gone? I don't get the issue if I mark stackVar static, or if I don't perform the assignment to foos[0].
Re: Conditional nothrow/safe/nogc/etc?
On Saturday, 30 January 2016 at 05:25:49 UTC, Rikki Cattermole wrote: On 30/01/16 6:17 PM, Matt Elkins wrote: [...] templated functions have attribute inference. Meaning if it can be nothrow it will be. Regarding your real use case, again struct if templated so it should be inferred. Convenient! Thanks!
Re: Conditional nothrow/safe/nogc/etc?
On Saturday, 30 January 2016 at 05:57:34 UTC, H. S. Teoh wrote: A common idiom that we use is to write an attributed unittest to verify that the function itself is @safe/etc.. This way, if instantiated with safe/etc. types, the template will also be safe/etc., but if instantiated with an unsafe type, it will correspondingly be unsafe (instead of failing to compile if you wrote @safe on the template). The unittest ensures that you do not accidentally introduce un-@safe code into the template and cause *all* instantiations to become un-@safe. auto mySafeCode(T)(T t) { ... } @safe unittest { auto x = mySafeCode(safeValue); } T Seems sound. Thanks!
Re: Relocatable objects and internal pointers
On Saturday, 30 January 2016 at 01:18:33 UTC, Ali Çehreli wrote: Definitely so. Rvalues are moved around all the time. The following program has two rvalue moves without calling post-blits or destructors. Oi, that makes life tough. Ok, I'll figure something else out, then... Thanks for the response!
Re: d plugin for Intelij Idea debuging support
On Friday, 29 January 2016 at 12:00:25 UTC, Pavel wrote: Hello! Is there any debuging support for Intelij Idea's D plugin? Thanks! I can't say for certain, but the website (https://github.com/kingsleyh/DLanguage) lists this as an upcoming feature by the end of 2016, so I think there probably isn't any support yet? I use this plugin, too, it's pretty awesome.