Re: Inconsistency between `AllMembers` and `hasMember`
On Sunday, 18 November 2018 at 02:37:13 UTC, Stanislav Blinov wrote: On Sunday, 18 November 2018 at 00:51:51 UTC, drug wrote: On 18.11.2018 1:26, Adam D. Ruppe wrote: That's because the compiler passes it a hidden pointer to refer to the context outside. The compiler could perhaps be smarter about it, and see if those methods actually refer to the context, but it seems to simply say if the method is there, it might refer to it and it adds the context pointer (note that S.sizeof increases too) and a magic constructor to set it. Well, if "this" means hidden pointer then it never should be returned by AllMembers at all, isn't it? It's only "hidden" in that there's no symbol to access it. But you can still access it via .tupleof, and it still of course affects the ABI (i.e. S.sizeof is always at least pointer size when S is nested). If you want to iterate fields, .tupleof is a better way to do it. As for that hidden pointer, you can just test with __traits(isNested, S) whether that's present, and just don't look at the last field. But in that case shouldn't you be able to tell whether it has it or not through hasMember?
Re: Inconsistency between `AllMembers` and `hasMember`
On Sunday, 18 November 2018 at 09:10:57 UTC, bauss wrote: On Sunday, 18 November 2018 at 02:37:13 UTC, Stanislav Blinov wrote: It's only "hidden" in that there's no symbol to access it... But in that case shouldn't you be able to tell whether it has it or not through hasMember? Yah, a case for inconsistency could be made here. IMHO it should just have some reserved name like __context.
Re: Inconsistency between `AllMembers` and `hasMember`
On 18.11.2018 5:37, Stanislav Blinov wrote: It's only "hidden" in that there's no symbol to access it. But you can still access it via .tupleof, and it still of course affects the ABI (i.e. S.sizeof is always at least pointer size when S is nested). If you want to iterate fields, .tupleof is a better way to do it. As for that hidden pointer, you can just test with __traits(isNested, S) whether that's present, and just don't look at the last field. But what is the reason for someone to use this pointer even via .tupleof? I still think AllMember shouldn't return it at all. Of course I can filter it using isNested or just comparing with "this" leteral during iteration. But it complicates things I believe. P.S. using isNested seems to be more portable solution than comparing with leteral but is this pointer always the last element in AllMember trait result?
Re: Inconsistency between `AllMembers` and `hasMember`
Reported: https://issues.dlang.org/show_bug.cgi?id=19410
Re: How do you debug @safe @nogc code? Can't figure out how to print.
On Saturday, 17 November 2018 at 21:56:23 UTC, Neia Neutuladh wrote: On Sat, 17 Nov 2018 21:16:13 +, aliak wrote: Could do. But it's not scalable. I'd have to comment out all the unittests that call the template function with a T that allocates inside the @nogc template (if I understood you correctly that it) I meant something like: void debugln(T...)(T args) @nogc { import std.stdio; debug(MyProject) writeln(args); } You use that function instead of writeln in your @nogc-compatible templates: void callFunc(alias func)() { debugln("about to call function!"); func(); debugln("done calling function!"); } Then I can write: @nogc: void foo() { printf("hello world\n"); } void main() { callFunc!foo(); } Aha! I misunderstood what you meant. Yes that's actually simpler that what I was doing :D Thanks!
Re: Making external types available to mixins
On Saturday, 17 November 2018 at 21:11:38 UTC, Adam D. Ruppe wrote: On Saturday, 17 November 2018 at 17:58:54 UTC, John Chapman wrote: Has anyone had a similar need and come up with a solution? You might be able to just pass it the Calendar type, and then fetch its parent module and get the ICalendarFactory from there (assuming they are defined in the same module). But generally speaking, passing strings to a mixin that refer to something in another module isn't going to work well thanks to scoping rules. You are better off passing a symbol of some sort. So there is no actual Calendar type. There's an ICalendarFactory type that creates instances of ICalendar (these types are part of a third-party API). "Calendar" is just a key users could use when calling a "makeWith" method that would build the ICalendar/Factory names, instantiate the factory, call the appropriate factory method and return the result. There are thousands of such object/factory pairs in the API. Just trying to cut out a lot of boilerplate code, but it doesn't seem doable this way.
difficulties with const structs and alias this / template functions
I'm making a fixed point numeric type and want it to work correctly with const. First problem: ``` const q16 a = 6; a /= 2; // compiles! despite `a` being const. writeln(a); // still 6 a.toQ32 /= 2;// what's actually happening ``` My q16 type has an implicit conversion to q32 (like how int can be converted to long): ``` q32 toQ32() const { return q32(...); } alias toQ32 this; ``` How do I make it so that a const(q16) will be converted to a const(q32) instead of mutable q32? Second problem: ``` Q log2(Q)(Q num) if (is(Q : q16) || is(Q : q32)) { import std.traits: Unqual; Unqual!Q x = num; // actual code } ``` When I call this with a const(q16), Q is resolved to const(q16) so I have to unqualify Q every time. It works, but feels clumsy. Is there an easier way to automatically de-const parameters? We're working with small value types here, it should be simple. If anyone knows any other pitfalls with const, I'd love to know them.
Neater enum + version
Currently I have something like version (Posix) { enum foo = "bar"; } else { enum foo = "baz"; } Is there anyway to make it "neater"? Maybe something in one line: enum foo = version (Posix) { "posix" } : { "other" } ;
Re: Neater enum + version
On Sun, 18 Nov 2018 17:47:07 +, Vladimirs Nordholm wrote: > Is there anyway to make it "neater"? Maybe something in one line: > > enum foo = version (Posix) { "posix" } : { "other" } ; If you're doing it often: T ifPosix(T)(T a, T b) { version (Posix) return a; else return b; } enum foo = ifPosix("posix", "other"); If it's a one-off thing, though, there's not much you can do.
Re: Neater enum + version
On Sunday, 18 November 2018 at 17:52:21 UTC, Neia Neutuladh wrote: On Sun, 18 Nov 2018 17:47:07 +, Vladimirs Nordholm wrote: Is there anyway to make it "neater"? Maybe something in one line: enum foo = version (Posix) { "posix" } : { "other" } ; If you're doing it often: T ifPosix(T)(T a, T b) { version (Posix) return a; else return b; } enum foo = ifPosix("posix", "other"); If it's a one-off thing, though, there's not much you can do. Ah. Thank you for the example, might use that some other time. Thanks :)
Re: difficulties with const structs and alias this / template functions
On Sunday, 18 November 2018 at 17:30:18 UTC, Dennis wrote: I'm making a fixed point numeric type and want it to work correctly with const. First problem: ``` const q16 a = 6; a /= 2; // compiles! despite `a` being const. Ouch. That's actually kind of nasty. writeln(a); // still 6 a.toQ32 /= 2;// what's actually happening ``` My q16 type has an implicit conversion to q32 (like how int can be converted to long): ``` q32 toQ32() const { return q32(...); } alias toQ32 this; ``` How do I make it so that a const(q16) will be converted to a const(q32) instead of mutable q32? Like this: // implement separate methods for mutable/const/immutable q32 toQ32() { return q32(x); } const(q32) toQ32() const { return q32(x); } immutable(q32) toQ32() immutable { return q32(x); } Or like this: // implement all three in one method, using the `this template` feature auto toQ32(this T)() { static if (is(T == immutable)) return immutable(q32)(x); else static if (is(T == const)) return const(q32)(x); else return q32(x); } Second problem: ``` Q log2(Q)(Q num) if (is(Q : q16) || is(Q : q32)) { import std.traits: Unqual; Unqual!Q x = num; // actual code } ``` When I call this with a const(q16), Q is resolved to const(q16) so I have to unqualify Q every time. It works, but feels clumsy. Is there an easier way to automatically de-const parameters? We're working with small value types here, it should be simple. Define different overloads for Q and const Q. Or this: Q log2(Q)(inout Q num) if (is(Q : q16) || is(Q : q32)) { /* ... */ } Being able to jam mutable/const/immutable implementation in one function like that should tell you that you shouldn't mutate the argument. Then, the necessity to Unqual will go away on it's own ;)
Re: Neater enum + version
On 2018-11-18 18:52, Neia Neutuladh wrote: On Sun, 18 Nov 2018 17:47:07 +, Vladimirs Nordholm wrote: Is there anyway to make it "neater"? Maybe something in one line: enum foo = version (Posix) { "posix" } : { "other" } ; If you're doing it often: T ifPosix(T)(T a, T b) { version (Posix) return a; else return b; } enum foo = ifPosix("posix", "other"); If it's a one-off thing, though, there's not much you can do. This can be generalized by passing in the version identifier as a string and do a string mixin. -- /Jacob Carlborg
Re: difficulties with const structs and alias this / template functions
On Sunday, 18 November 2018 at 18:17:54 UTC, Stanislav Blinov wrote: // implement separate methods for mutable/const/immutable Thanks. I should have tried that, but I assumed it wouldn't work since you can't overload on return-type only. However, the const / non-const makes it allowed. Or like this: // implement all three in one method, using the `this template` feature That's new to me, interesting. Define different overloads for Q and const Q. Or this: Q log2(Q)(inout Q num) if (is(Q : q16) || is(Q : q32)) { /* ... */ } Being able to jam mutable/const/immutable implementation in one function like that should tell you that you shouldn't mutate the argument. Then, the necessity to Unqual will go away on it's own ;) Different overloads sounds like a lot of boilerplate. inout still results in "cannot modify `inout` expression `input`" My goal is to be able to write straightforward and correct signatures for fixed point functions that receive mutable copies of whatever they are fed. This isn't even limited to my custom types: ``` T f0(T)(T x, T y) {return x += y;} int f1(int x, int y) {return x += y;} long f1(long x, long y) {return x += y;} void main() { import std.stdio; writeln(f0(const(int)(3), const(long)(4))); // can't modify const writeln(f1(const(int)(3), const(long)(4))); // fine } ``` The explicit overloads of f1 work fine, but the generic f0 does not. I could just use the f1 strategy of explicitly making overloads, but then adding a q64 or q128 type would mean having to double the existing boilerplate everywhere. I would like to have a `isFixedPoint` template and generic functions that take any isFixedPoint!Q, but it seems I have to manually enforce "don't forget to unqual Q first!".
Re: difficulties with const structs and alias this / template functions
On Sunday, 18 November 2018 at 17:30:18 UTC, Dennis wrote: I'm making a fixed point numeric type and want it to work correctly with const. First problem: ``` const q16 a = 6; a /= 2; // compiles! despite `a` being const. writeln(a); // still 6 a.toQ32 /= 2;// what's actually happening ``` My q16 type has an implicit conversion to q32 (like how int can be converted to long): ``` q32 toQ32() const { return q32(...); } alias toQ32 this; ``` How do I make it so that a const(q16) will be converted to a const(q32) instead of mutable q32? Second problem: ``` Q log2(Q)(Q num) if (is(Q : q16) || is(Q : q32)) { import std.traits: Unqual; Unqual!Q x = num; // actual code } ``` When I call this with a const(q16), Q is resolved to const(q16) so I have to unqualify Q every time. It works, but feels clumsy. Is there an easier way to automatically de-const parameters? We're working with small value types here, it should be simple. If anyone knows any other pitfalls with const, I'd love to know them. Yah most people tend to avoid const for this reason. It only really works for basic types, if you have a "const int" you can convert it to an "int" by copy. But if you have a type like Vector!(const int) that won't work, you can't even convert Vector!int to Vector!(const int) easily for example. ``` Q log2(Q)(Q num) if (is(Q : q16) || is(Q : q32)) { import std.traits: Unqual; Unqual!Q x = num; // actual code } ``` This is pretty much the only way, you can just add alias V = Unqual!Q; then use V in your function instead of Unqual!Q everywhere.
Re: difficulties with const structs and alias this / template functions
On Sunday, 18 November 2018 at 22:30:52 UTC, Rubn wrote: Yah most people tend to avoid const for this reason. It only really works for basic types, if you have a "const int" you can convert it to an "int" by copy. But if you have a type like Vector!(const int) that won't work, you can't even convert Vector!int to Vector!(const int) easily for example. That's unfortunate. I can relate better to Jonathan's article [1] now. But I'll still try to make it working with const since other people might want to use it. I'm also trying to make it work with immutable, and from BigInt [2] I learned that constructors need to be `pure` for creating immutable objects. (I don't know why.) Are there any other gotchas? I didn't add an immutable variant for toQ32 like Stanislav suggested, but creating an immutable q32 from a q16 still seems to work fine. [1] http://jmdavisprog.com/articles/why-const-sucks.html [2] https://issues.dlang.org/show_bug.cgi?id=17330
Re: opDispatch doesn't play nice with inheritance
On Saturday, November 17, 2018 11:09:51 PM MST Carl Sturtivant via Digitalmars-d-learn wrote: > On Thursday, 15 November 2018 at 19:01:45 UTC, Ali Çehreli wrote: > > On 11/15/2018 09:14 AM, Carl Sturtivant wrote: > > > opDispatch is special in that it allows for functions to be > > > > added to a > > > > > class or struct when undefined overtly but used elsewhere but > > > > it seems > > > > > those functions sadly are final. > > > > > > Can anything useful be done to remedy the situation? > > > > For the compiler to be able to make all opDispatch > > instantiations virtual, it would have to first see all calls > > that generate opDispatch instantiations. (Impossible in the > > presence of separate compilation.) > > > > Only then the compiler would know how large the vtbl of the > > base class should be and what member functions of the derived > > class are overrides of those virtual functions. > > I suppose it's such administrative difficulties that led to D > defining anything that might conceivably be overridden to be > virtual, whether or not actually overridden. The issue with templated functions is that you don't have the full list of instantiations when the base class is compiled, whereas as I understand it, the issue with virtual-by-default is more of an issue of choosing code correctness by default over efficiency by default. You can get some fun, subtle bugs in C++ when you call non-virtual functions in circumstances where they were overridden and used as if they were virtual. So, by making all public and protected class member functions virtual by default and not allowing non-virtual functions to be overridden, it prevents a whole class of bugs. It _does_ have some performance downsides in that it easily leads to functions being virtual when they don't need to be, which can be gotten around with some extra effort, but it's effectively favoring correctness by default over efficiency. And for a lot of programs, it's a great tradeoff, especially when the whole point of classes in D is to use inheritance and polymorphism, and the cases where you don't need it, you use a struct. For the rest, final can be used to devirtualize a function as long as it's not overriding a function. To avoid bugs in the same way that we currently do without having virtual be the default, we probably would have had to make it illegal to override any class functions unless they were explicitly virtual, which would have created a different set of problems. And whether that would be better or worse is a matter of debate. In the end, it's a matter of tradeoffs, and which is better is likely to depend on how your applications benefit from and use the feature. If most of your class functions are used polymorphically, then having to mark them as virtual or having to put virtual: at the top of your class would get really annoying, whereas if you typically have only a few virtual functions and then lots of non-virtual property functions (like the AAA guys apparently like to do), then having to mark things with final to devirtualize them is very annoying. There's no pleasing everyone. Either way, allowing overriding non-virtual functions like C++ does would just be begging for bugs. - Jonathan M Davis
Re: difficulties with const structs and alias this / template functions
On Sunday, 18 November 2018 at 20:10:52 UTC, Dennis wrote: On Sunday, 18 November 2018 at 18:17:54 UTC, Stanislav Blinov wrote: Q log2(Q)(inout Q num) if (is(Q : q16) || is(Q : q32)) { /* ... */ } Being able to jam mutable/const/immutable implementation in one function like that should tell you that you shouldn't mutate the argument. Then, the necessity to Unqual will go away on it's own ;) Different overloads sounds like a lot of boilerplate. inout still results in "cannot modify `inout` expression `input`" You just dismissed that second to last sentence, did you? :) My goal is to be able to write straightforward and correct signatures for fixed point functions that receive mutable copies of whatever they are fed. This isn't even limited to my custom types: ``` T f0(T)(T x, T y) {return x += y;} int f1(int x, int y) {return x += y;} long f1(long x, long y) {return x += y;} ``` The explicit overloads of f1 work fine, but the generic f0 does not. ``` T f0(T)(inout T x, inout T y) { return x + y; } ``` ;) But if you really really want to mutate the argument, then handling different mutability for T is the only way: ``` T f0(T)(T x, T y) { import std.traits : isMutable; static if (isMutable!T) return x += y; else return x + y; } ``` I know it's a bit painful though. In fact, Phobos also suffers from it. In std.numeric: T gcd(T)(T a, T b) if (isIntegral!T) { static if (is(T == const) || is(T == immutable)) { return gcd!(Unqual!T)(a, b); } // ... } Not only that looks ugly, but (with DMD) it makes gcd a doulbe function call :D
Re: difficulties with const structs and alias this / template functions
On Monday, 19 November 2018 at 00:50:28 UTC, Dennis wrote: I'm also trying to make it work with immutable, and from BigInt [2] I learned that constructors need to be `pure` for creating immutable objects. (I don't know why.) That's only for types with indirections (pointers), since `pure` guarantees that you do not mutate any global state. For value types, they work just fine: struct q16 { uint x; this(uint x) { this.x = x; } this(uint x) const { this.x = x; } this(uint x) immutable { this.x = x; } // or again, all three packed together: this(this T)(uint x) { this.x = x; } } Are there any other gotchas? I didn't add an immutable variant for toQ32 like Stanislav suggested, but creating an immutable q32 from a q16 still seems to work fine. Yup, that's because, like Rubn said, copying value types is trivial. Where it all comes to bite you is when you start having pointers, because you can't copy a const(T)* into a T*.
Re: difficulties with const structs and alias this / template functions
On Monday, 19 November 2018 at 01:13:29 UTC, Stanislav Blinov wrote: You just dismissed that second to last sentence, did you? :) I don't know what you mean with it. It's not that I'm trying to be sneaky or lazy really trying to modify the const parameter when I should treat it properly. And as the author I'm fine with Unqualing everything if that's needed, my concern is when another person using my type tries to write his own function: ``` q16 pow(q16 base, int exponent) { q16 result = 1; foreach(i; 0..exponent) result *= base; return result; } const q16 x = 3; writeln(pow(x, 3)); //works! ``` He then wants to make it more generic, so he rewrites: ``` Q pow(Q base, int exponent) if (isFixedPoint!Q) { Q result = 1; foreach(i; 0..exponent) result *= base; return result; } ``` And initially it seems to work, but as soon as it is used with const it breaks as `result` can't be mutated anymore. I'd like to set the example of writing proper generic functions, and if there is something simpler than importing Unqual I'd prefer that over my current solution. If there isn't, I'll just need to write a "don't forget to Unqual const" comment. ``` T f0(T)(inout T x, inout T y) { return x + y; } ``` ;) What does inout do here? If the return type is also inout(T) I know that the return type gets the same qualifiers as inout parameters. But in this example, the return value is still T. I know it's a bit painful though. In fact, Phobos also suffers from it. In std.numeric: Yuck!
Re: difficulties with const structs and alias this / template functions
On Monday, 19 November 2018 at 01:24:02 UTC, Stanislav Blinov wrote: Yup, that's because, like Rubn said, copying value types is trivial. Where it all comes to bite you is when you start having pointers, because you can't copy a const(T)* into a T*. I'm not using reference types, but still: ``` struct S { int a; this(int a) { this.a = a; } } void main() { immutable S d = 3; } ``` onlineapp.d(10): Error: mutable method onlineapp.S.this is not callable using a immutable object onlineapp.d(10):Consider adding const or inout to onlineapp.S.this const still leaves the first error, inout works though I don't know what it does. Adding pure also works.
Re: difficulties with const structs and alias this / template functions
On Monday, 19 November 2018 at 02:03:18 UTC, Dennis wrote: On Monday, 19 November 2018 at 01:13:29 UTC, Stanislav Blinov wrote: You just dismissed that second to last sentence, did you? :) I don't know what you mean with it. It's not that I'm trying to be sneaky or lazy really trying to modify the const parameter when I should treat it properly. And as the author I'm fine with Unqualing everything if that's needed, my concern is when another person using my type tries to write his own function: ``` q16 pow(q16 base, int exponent) { q16 result = 1; foreach(i; 0..exponent) result *= base; return result; } const q16 x = 3; writeln(pow(x, 3)); //works! ``` He then wants to make it more generic, so he rewrites: ``` Q pow(Q base, int exponent) if (isFixedPoint!Q) { Q result = 1; foreach(i; 0..exponent) result *= base; return result; } ``` And initially it seems to work, but as soon as it is used with const it breaks as `result` can't be mutated anymore. Yes, but that's not the problem with your type. It's a problem with the user not realizing that const is a type qualifier. I'd like to set the example of writing proper generic functions, and if there is something simpler than importing Unqual I'd prefer that over my current solution. If there isn't, I'll just need to write a "don't forget to Unqual const" comment. ``` T f0(T)(inout T x, inout T y) { return x + y; } ``` ;) What does inout do here? You're right, it's not needed there at all. If the return type is also inout(T) I know that the return type gets the same qualifiers as inout parameters. But in this example, the return value is still T. Because, as before, value types are all copyable between mutable/const/immutable. So even if `return x + y` would yield a `const T`, you can still instantiate a T from it.
Re: difficulties with const structs and alias this / template functions
On Monday, 19 November 2018 at 02:08:14 UTC, Dennis wrote: On Monday, 19 November 2018 at 01:24:02 UTC, Stanislav Blinov wrote: Yup, that's because, like Rubn said, copying value types is trivial. Where it all comes to bite you is when you start having pointers, because you can't copy a const(T)* into a T*. I'm not using reference types, but still: ``` struct S { int a; this(int a) { this.a = a; } } void main() { immutable S d = 3; } ``` onlineapp.d(10): Error: mutable method onlineapp.S.this is not callable using a immutable object onlineapp.d(10):Consider adding const or inout to onlineapp.S.this const still leaves the first error... You're skimming the examples ;) struct S { int a; this(int a) { this.a = a; } this(int a) const { this.a = a; } this(int a) immutable { this.a = a; } } or struct S { int a; this(this T)(int a) { this.a = a; } } ...inout works though I don't know what it does. Recall that member functions (including constructors) are just functions in disguise: struct S { this(int a) inout { /* ... */ } } what that boils down to, conceptually, is: void _compiler_made_it_a_struct_S_constructor(ref inout S this, int a); In other words, an `inout` method makes the "this" reference an `inout`. Same goes for const, immutable, etc.
D vs perl6
hi, What if D compared with the latest perl6? well, I know that perl6 is a VM targeted language. so which has the greater modeling-power? And perl6 seems is selling for Concurrency[1], but I think D will do this far better than a VM targeted language, right? with the pre-released version of perl6 publicity, perl6 will be a powerful language with features like "lazy and eager list evaluation", but after learned D, look back to perl6, I found very few things that means "powerful". So, can you experts give a more comprehensive compare with perl6 and D? speed? modeling-power? concurrency? package-management dub vs CPAN? ... Thanks! [1]: https://www.evanmiller.org/why-im-learning-perl-6.html