Re: UFCS in generic libraries, silent hijacking, and compile errors.
On Saturday, 10 March 2018 at 23:00:07 UTC, Jonathan M Davis wrote: The idea is that the type can provide its own version of the function that is better optimized for it - e.g. it could potentially provide a member function find that is more efficient for it than std.algorithm.searching.find. That's actually the only technical reason why UFCS is superior to the normal function call syntax. I think this may have hit the nail on the spot. So basically my thinking is that if you're going to use UFCS inside a generic function where you can't know what kind of methods the type has, do not do it unless you want this particular behavior or if your intent is to use a type's known API. issue in practice. That doesn't mean that it's never a problem, but from what I've seen, it's very rarely a problem, and it's easy to work around if you run into a particular case where it is a problem. Ya, it's easy to work around but the caveat there is you need to realize it's happening first, and add that to that it's "rarely a problem" and well ... now it seems scary enough for this to mentioned somewhere I'd say. The one case that I am aware of where best practice is to avoid UFCS is with put for output ranges, but that has nothing to with your concerns here. Rather, it has to do with the fact that std.range.primitives.put has a lot of overloads for handling various arguments (particularly when handling ranges of characters), and almost no one implements their output ranges with all of those overloads. So, if you use put with UFCS, you tend to run into problems if you do anything other than put a single element of the exact type at a time, whereas the free function handles more cases (even if they ultimately end up calling that member function with a single argument of the exact type). We probably shouldn't have had the free function and the member function share the same name. Oh, can you share a short example here maybe? Not sure I followed completely Is it basically: // if r is output range r.put(a, b) // don't do this? put(r, a, b) // but do this? (Cause compilation error) Is there something I'm not seeing as to why UFCS is not part of the overload set, and is there a way other than not using UFCS to prevent the silent hijacking? If it were part of the overload set, then you have problems calling member functions, particularly because there is no way to call a member function other than with UFCS, whereas you can call free functions without UFCS, and if you _really_ want to be sure that a very specific function is called, then you can even give its entire module path when calling it. When UFCS was introduced, it was decided that having member functions always win out would cause the fewest problems. - Jonathan M Davis Ah yes, ok this makes sense. Follow up: How about if it's not part of the overload set, but is looked up if the function does not exist in the overload set. What would the problems there be? Basically I don't see a reason why we wouldn't want the following to work: struct S { void f() {} } void f(S s, int i) {} S().f(3); // error Thanks!
How do you call an eponymous template that has a secondary template arg?
Eg: template aliasOf(T) { enum aliasOf(alias a) = is(typeof(a) == T); } The use case for this is for std.meta.allSatisfy for variadic args, i.e. template T(values...) if (allSatisfy!(aliasOf!string, values) { ... } But how do you call that template otherwise? I've tries: * aliasOf!int!"string" // multiple ! arguments are not allowed * (aliasOf!int)!"string" // error c-style cast * aliasOf!int.aliasOf!"string" // template isAliasOf(alias a) does not have property 'isAliasOf I can work around this by: template typeOf(T) { enum isAliasedBy(alias a) = is(typeof(a) == T); } and then do: template T(values...) if (allSatisfy!(typeOf!string.isAliasedBy, values) { ... } But I like the readability of the former better if there's a way to achieve it? Cheers - Ali
Re: Forwarding arguments through a std.algorithm.map
On Saturday, 10 March 2018 at 20:48:06 UTC, Nordlöw wrote: If I have a function bool f(Rs...)(Rs rs) is it somehow possible to map and forward all its arguments `rs` to another function bool g(Rs...)(Rs rs); through a call to some map-and-forward-like-function `forwardMap` in something like bool f(Rs...)(Rs rs) { alias someArbitraryFun = _ => _; return g(forwardMap!(someArbitraryFun)(rs)); } ? What should the definition of forwardMap look like? Does Phobos contain something like this already? Not sure whether I understand what you are looking for, but std.functional is your friend here I think pipe/compose is the function you are searching for. https://dlang.org/phobos/std_functional.html#forward https://dlang.org/phobos/std_functional.html#partial https://dlang.org/phobos/std_functional.html#pipe
Re: Error compiling with DUB (dmd) since yesterday dub update
On Sunday, 11 March 2018 at 07:17:58 UTC, Mario wrote: On Sunday, 11 March 2018 at 07:11:09 UTC, rikki cattermole wrote: On 11/03/2018 8:02 PM, Mario wrote: On Sunday, 11 March 2018 at 06:59:32 UTC, rikki cattermole wrote: On 11/03/2018 7:55 PM, Mario wrote: [...] Are you aware that it is compiling with ldc? Actually, I didn't even realize XD. I'm a newbie in VibeD and DLang, should I change it? Why did compiling work yesterday? Thanks in advance Workaround switch to dmd. Real solution please file a bug for Botan and attach your log in. Thank you, I followed your advice and I created an issue on the GitHub project: https://github.com/etcimon/botan/issues/42 Or simply use the built-in openssl 1.1?
Inner Classes vs. Inner Structs
This works: ``` class S { int n, m; int sum() { return n + m; } Inner!(sum) a; class Inner(alias f){ auto get() { return f(); } } } ``` This doesn't: ``` struct S { int n, m; int sum() { return n + m; } Inner!(sum) a; struct Inner(alias f){ auto get() { return f(); // Error: this for sum needs to be type S not type Inner!(sum) } } } ``` The only difference between the two is one one uses classes, the other uses structs. My question is, under the current semantics of D, shouldn't the two work the same? That is, shouldn't the inner struct in the second example have an implicit context reference to the outer struct? Is this a bug? Thanks for the help, Mike
Re: Forwarding arguments through a std.algorithm.map
On Saturday, 10 March 2018 at 20:48:06 UTC, Nordlöw wrote: If I have a function bool f(Rs...)(Rs rs) is it somehow possible to map and forward all its arguments `rs` to another function bool g(Rs...)(Rs rs); through a call to some map-and-forward-like-function `forwardMap` in something like bool f(Rs...)(Rs rs) { alias someArbitraryFun = _ => _; return g(forwardMap!(someArbitraryFun)(rs)); } ? What should the definition of forwardMap look like? Does Phobos contain something like this already? there is staticMap.
Re: How do you call an eponymous template that has a secondary template arg?
On Sunday, 11 March 2018 at 12:05:56 UTC, aliak wrote: Eg: template aliasOf(T) { enum aliasOf(alias a) = is(typeof(a) == T); } The use case for this is for std.meta.allSatisfy for variadic args, i.e. template T(values...) if (allSatisfy!(aliasOf!string, values) { ... } But how do you call that template otherwise? I've tries: * aliasOf!int!"string" // multiple ! arguments are not allowed * (aliasOf!int)!"string" // error c-style cast * aliasOf!int.aliasOf!"string" // template isAliasOf(alias a) does not have property 'isAliasOf I can work around this by: template typeOf(T) { enum isAliasedBy(alias a) = is(typeof(a) == T); } and then do: template T(values...) if (allSatisfy!(typeOf!string.isAliasedBy, values) { ... } But I like the readability of the former better if there's a way to achieve it? Cheers - Ali The first version works here: ``` template aliasOf(T) { enum aliasOf(alias a) = is(typeof(a) == T); } string s; pragma(msg, allSatisfy!(aliasOf!string, s, "string")); ``` Now on the fact that what is done is correct is another story. If the literal passed is supposed to be a type then it's clearly wrong. You'd have to mix it: ``` template aliasOf(T) { template aliasOf(alias a) { mixin("alias A = " ~ a ~ ";"); enum aliasOf = is(A == T); } } alias myString1 = string; alias myString2 = string; pragma(msg, allSatisfy!(aliasOf!string, "myString1", "myString2")); ```
Re: UFCS in generic libraries, silent hijacking, and compile errors.
On Sunday, March 11, 2018 08:39:54 aliak via Digitalmars-d-learn wrote: > On Saturday, 10 March 2018 at 23:00:07 UTC, Jonathan M Davis > > issue in practice. That doesn't mean that it's never a problem, > > but from what I've seen, it's very rarely a problem, and it's > > easy to work around if you run into a particular case where it > > is a problem. > > Ya, it's easy to work around but the caveat there is you need to > realize it's happening first, and add that to that it's "rarely a > problem" and well ... now it seems scary enough for this to > mentioned somewhere I'd say. You're talking about a situation where you used a function whose parameters match that of a member function exactly enough that a member function gets called instead of a free function. That _can_ happen, but in most cases, there's going to be a mismatch, and you'll get a compiler error if the type defines a member function that matches the free function. I don't think that I have ever seen that happen or ever seen anyone complain about it. The only case I recall along those lines was someone who was trying to use a free function that they'd decided to call front instead of something else, and it had parameters beyond just the input range, so that programmer got compilation errors when they tried to use it in their range-based functions. I think that this is really a theoretical concern and not a practical one. Certainly, it's really only going to potentially be an issue in library code that gets used by a ton of folks with completely unexpected types. If it's in your own code, you're usually well aware of what types are going to be used with a generic function, and proper testing would catch the rare case where there would be a problem. If you're really worried about it, then just don't use UFCS, but for better or worse, it seems to be the case that the vast majority of D programmers use UFCS all the time and don't run into problems like this. > > The one case that I am aware of where best practice is to avoid > > UFCS is with put for output ranges, but that has nothing to > > with your concerns here. Rather, it has to do with the fact > > that std.range.primitives.put has a lot of overloads for > > handling various arguments (particularly when handling ranges > > of characters), and almost no one implements their output > > ranges with all of those overloads. So, if you use put with > > UFCS, you tend to run into problems if you do anything other > > than put a single element of the exact type at a time, whereas > > the free function handles more cases (even if they ultimately > > end up calling that member function with a single argument of > > the exact type). We probably shouldn't have had the free > > function and the member function share the same name. > > Oh, can you share a short example here maybe? Not sure I followed > completely > > Is it basically: > > // if r is output range > > r.put(a, b) // don't do this? > > put(r, a, b) // but do this? > > (Cause compilation error) Essentially yes, though you're passing too many arguments to put. There are cases where put(output, foo) will compile while output.put(foo) will not. In particular, std.range.primitives.put will accept both individual elements to be written to the output range and ranges of elements to be written, whereas typically, an output range will be written to only accept an element at a time. It's even more extreme with output ranges of characters, because the free function put will accept different string types and convert them, and even if the programmer who designed the output range added various overloads to put for completeness, it's enough extra work to deal with all of the various character types that they probably didn't. And put also works with stuff like delegates (most frequently used with a toString that accepts an output range), which don't have member functions. So, if you write your generic code to use the member function put, it's only going to work with user-defined types that define the particular overload(s) of put that you're using in your function, whereas if you use the free function, you have more variety in the types of output ranges that your code works with, and you have more ways that you can call put (e.g. passing a range of elements instead of a single element). > How about if it's not part of the overload set, but is looked up > if the function does not exist in the overload set. What would > the problems there be? > > Basically I don't see a reason why we wouldn't want the following > to work: > > struct S { void f() {} } > void f(S s, int i) {} > S().f(3); // error So, are you complaining that it's an error, or you want it to be an error? As it stands, it's an error, because as far as the compiler is concerned, you tried to call a member function with an argument that it doesn't accept. If you want that code to work, then it would have to add the free function to the overload set while somehow leaving out the overloads that
Re: Inner Classes vs. Inner Structs
On Sunday, March 11, 2018 13:19:39 Mike Franklin via Digitalmars-d-learn wrote: > This works: > > ``` > class S { > int n, m; > int sum() { return n + m; } > Inner!(sum) a; > > class Inner(alias f){ > auto get() { > return f(); > } > } > } > ``` > > This doesn't: > > ``` > struct S { > int n, m; > int sum() { return n + m; } > Inner!(sum) a; > > struct Inner(alias f){ > auto get() { > return f(); // Error: this for sum needs to be type S > not type Inner!(sum) > } > } > } > ``` > > The only difference between the two is one one uses classes, the > other uses structs. My question is, under the current semantics > of D, shouldn't the two work the same? That is, shouldn't the > inner struct in the second example have an implicit context > reference to the outer struct? Is this a bug? No, it's on purpose, and the only reason that non-static, nested classes have access to the class they're declared in is for compatibility with Java, which doesn't have structs. Also, I suspect that if D were being designed now, it would treat nested classes the same as nested structs rather than treating nested structs like it currently does nested classes. Some of D's design has a number of Java-isms because of when D was first started and the fact that it didn't have some of features that it has now. Certainly, if we included the feature at this point if we were redesigning the language, I expect that it would only be to make porting Java code easier. Regardless, changing the current behavior would break code, so I doubt that it's going to change. IIRC, DWT relies on the behavior with classes (since it's code ported from Java), and while I question that all that much D code outside of DWT takes advantage of non-static, nested classes, I suspect that having non-static, nested structs suddenly act differently from static, nested structs would break a fair bit of D code. There is of course no way to know how much code would break with either change, but I doubt that Walter would agree to the potential code breakage in either direction. - Jonathan M Davis
Re: How do you call an eponymous template that has a secondary template arg?
On Sunday, 11 March 2018 at 13:44:38 UTC, Basile B. wrote: The first version works here: ``` template aliasOf(T) { enum aliasOf(alias a) = is(typeof(a) == T); } string s; pragma(msg, allSatisfy!(aliasOf!string, s, "string")); ``` I can see that my description was a little confusing, sorry about that, I meant to ask how would you call that without using the allSatisfy meta template. If I were to call it as a stand alone, ie: writeln(aliasOf!stringtemplate?>); I hope that makes it clearer. Now on the fact that what is done is correct is another story. If the literal passed is supposed to be a type then it's clearly wrong. You'd have to mix it: ``` template aliasOf(T) { template aliasOf(alias a) { mixin("alias A = " ~ a ~ ";"); enum aliasOf = is(A == T); } } alias myString1 = string; alias myString2 = string; pragma(msg, allSatisfy!(aliasOf!string, "myString1", "myString2")); ``` Aye, I see what you mean, but it is supposed to be a literal of a specific type. I.e. 3, "some string", SomeType(), etc. So aliasOf!int.aliasOf!3 // true Also, if I define it like this: template aliasOf(T) { auto aliasOf(U)(U) { return is(U == T); } } Then at least I can call it like: writeln(aliasOf!int(3)); // prints true But then I can't do: allSatisfy!(aliasOf!int, 3) I guess because it's a function now and not a template anymore so can't be used by allSatisfy.
core.stdc.stdlib._compare_fp_t and qsort
I'm getting a compiler error in a qsort() call as follows: qsort(recs, num_recs, (Record *).sizeof, compar); Record is a struct, recs is a fixed array of pointers to Record's and num_recs is a size_t that holds the number of valid records. compar is this: int compar(const void *p1, const void *p2) { import core.stdc.string : strcmp; const Record **rp1 = cast(Record **)p1; const Record **rp2 = cast(Record **)p2; return strcmp((*rp1).name.ptr, (*rp2).name.ptr); } The error is: Error: function testd.compar (const(void*) p1, const(void*) p2) is not callable using argument types () I don't quite understand what those parentheses mean: is it implying "no arguments" and if so, where would one provide arguments? Joe
Re: core.stdc.stdlib._compare_fp_t and qsort
On Sunday, 11 March 2018 at 23:12:30 UTC, Joe wrote: I'm getting a compiler error in a qsort() call as follows: qsort(recs, num_recs, (Record *).sizeof, compar); Record is a struct, recs is a fixed array of pointers to Record's and num_recs is a size_t that holds the number of valid records. compar is this: int compar(const void *p1, const void *p2) { import core.stdc.string : strcmp; const Record **rp1 = cast(Record **)p1; const Record **rp2 = cast(Record **)p2; return strcmp((*rp1).name.ptr, (*rp2).name.ptr); } The error is: Error: function testd.compar (const(void*) p1, const(void*) p2) is not callable using argument types () I don't quite understand what those parentheses mean: is it implying "no arguments" and if so, where would one provide arguments? Joe You have to pass a pointer to the function. Otherwise it'll be a parenthsis-less call. use : qsort(recs, num_recs, (Record *).sizeof, &compar);
Re: core.stdc.stdlib._compare_fp_t and qsort
On Sunday, 11 March 2018 at 23:26:04 UTC, Stefan Koch wrote: You have to pass a pointer to the function. Otherwise it'll be a parenthsis-less call. use : qsort(recs, num_recs, (Record *).sizeof, &compar); After passing a pointer, getting some other error messages, I changed the call to qsort(cast(void *)recs, num_recs, cast(size_t)(Record *).sizeof, &compar); and the latest error is: Error: function core.stdc.stdlib.qsort (scope void* base, ulong nmemb, ulong size, extern (C) int function(scope const(void*), scope const(void*)) @system compar) is not callable using argument types (void*, ulong, ulong, int function(const(void*) p1, const(void*) p2)) I fail to see which argument is causing the problem now. Joe
Re: core.stdc.stdlib._compare_fp_t and qsort
On Monday, 12 March 2018 at 01:04:06 UTC, Joe wrote: and the latest error is: D's error messages are so bad and shouldn't be hard to fix. It kills me that basic every-day functionality like this isn't a priority to the core devs. I even wrote a patch myself that would call this out but it didn't fit the code style so rejected. Ugh. But behold: (scope void* base, ulong nmemb, ulong size, extern (C) int function(scope const(void*), scope const(void*)) @system compar) (void*, ulong, ulong, int function(const(void*) p1, const(void*) p2)) I just reformatted it but now the difference should be visible: `extern(C)` is missing on your callback. The scope things might make a difference too, but I know for sure extern(C) is necessary on your callback function.
Re: core.stdc.stdlib._compare_fp_t and qsort
On Mon, Mar 12, 2018 at 01:04:06AM +, Joe via Digitalmars-d-learn wrote: > On Sunday, 11 March 2018 at 23:26:04 UTC, Stefan Koch wrote: > > You have to pass a pointer to the function. > > Otherwise it'll be a parenthsis-less call. > > use : qsort(recs, num_recs, (Record *).sizeof, &compar); > > After passing a pointer, getting some other error messages, I changed > the call to > > qsort(cast(void *)recs, num_recs, cast(size_t)(Record *).sizeof, > &compar); > > and the latest error is: > > Error: function core.stdc.stdlib.qsort (scope void* base, ulong nmemb, ulong > size, extern (C) int function(scope const(void*), scope const(void*)) > @system compar) is not callable using argument types (void*, ulong, ulong, > int function(const(void*) p1, const(void*) p2)) > > I fail to see which argument is causing the problem now. [...] I wonder if the problem is because qsort expects a C linkage function (`extern (C) int function(...)`), but you're passing a D linkage (i.e., native) function to it. You can't do that, because the order of arguments in a D linkage function may differ from a C linkage function, and could potentially cause a subtle bug if you're not expecting the arguments in a different order. (I actually ran into this myself, in a different context where there was no type enforcement like there is here, and it took me a long time to track down the problem.) If indeed this is the problem, try adding `extern (C)` to your D function to make it compatible with C linkage, and that should do the trick. I think. T -- If you look at a thing nine hundred and ninety-nine times, you are perfectly safe; if you look at it the thousandth time, you are in frightful danger of seeing it for the first time. -- G. K. Chesterton
Date range iteration
I wanted to iterate through a date range, so I initially tried: iota(Date(2016,1,1),Date(2018,1,1),dur!"days"(1)); That wouldn't compile, which is fair enough I guess. So I tried a for loop: for (auto i = Date(2016,1,1); i < Date(2018,1,1); i+=dur!"days"(1)){} That seemed to work fine, but I remember reading "for loops are bad" somewhere. So I looked for a range type mechanism, and I think it's this: foreach (i; Interval!Date (Date(2016,1,1),Date(2018,1,1)).fwdRange ( (a) { return a+dur!"days"(1); })){} My question is if the third way is the proper way of stepping through a period of time? It just seems quite complicated to me. Thanks, Jordan
Re: core.stdc.stdlib._compare_fp_t and qsort
On Monday, 12 March 2018 at 01:45:54 UTC, Adam D. Ruppe wrote: I just reformatted it but now the difference should be visible: `extern(C)` is missing on your callback. The scope things might make a difference too, but I know for sure extern(C) is necessary on your callback function. I saw the extern(C) and I believe I tried it before my previous post, but dismissed it because I saw no difference in compiler behavior. In any case declaring extern (C) int compar(const (void *) p1, const (void *) p2) { ... // as before } still gives the error: function core.stdc.stdlib.qsort (scope void* base, ulong nmemb, ulong size, extern (C) int function(scope const(void*), scope const(void*)) @system compar) is not callable using argument types (void*, ulong, ulong, extern (C) int function(const(void*) p1, const(void*) p2)) It only went away with extern (C) int compar(scope const (void *) p1, scope const (void *) p2) Could you explain or direct me to something that elucidates why the "scope" qualifiers are needed? And is there something else that is necessary inside compar() or are the "scope"s just for decorative purposes? Joe
Re: Date range iteration
On Monday, March 12, 2018 02:11:49 Jordan Wilson via Digitalmars-d-learn wrote: > I wanted to iterate through a date range, so I initially tried: > iota(Date(2016,1,1),Date(2018,1,1),dur!"days"(1)); > > That wouldn't compile, which is fair enough I guess. Maybe iota should be made to work, but as present, it basically wants all three of the types it's given to be the same or implicitly convertible to a single type. It can't handle the step being a completely different type. > So I tried a for loop: > for (auto i = Date(2016,1,1); i < Date(2018,1,1); > i+=dur!"days"(1)){} > > That seemed to work fine, but I remember reading "for loops are > bad" somewhere. for loops are just fine - especially if you're just going to loop through all the values and do something to them - but if you have a range, it's a lot more flexible. > So I looked for a range type mechanism, and I > think it's this: > foreach (i; Interval!Date > (Date(2016,1,1),Date(2018,1,1)).fwdRange ( (a) { return > a+dur!"days"(1); })){} > > My question is if the third way is the proper way of stepping > through a period of time? It just seems quite complicated to me. Well, honestly, the range support in std.datetime isn't very good. The core problem that overcomplicates it is that the Interval needs to know which direction you want to iterate in, and then the helper functions tend to need to know it too in order to work properly. Also, the time point type being used also tends to get duplicated a fair bit. So, you end up with annoyingly repetitive code. The helper function intended for this use case is https://dlang.org/phobos/std_datetime_interval.html#everyDuration So, you'd get something more like auto interval = Interval!Date(Date(2016, 1, 1), Date(2018, 1, 1)); auto range = interval.fwdRange(everyDuration!Date(days(1))); But if you're just going to use the range in a foreach loop, then you might as well just use a for loop. All of this extra machinery only really starts being valuable when you start feeding the ranges into range-based functions. For a simple loop, it's overkill. - Jonathan M Davis
Re: core.stdc.stdlib._compare_fp_t and qsort
On Sunday, 11 March 2018 at 23:12:30 UTC, Joe wrote: I'm getting a compiler error in a qsort() call as follows: qsort(recs, num_recs, (Record *).sizeof, compar); Record is a struct, recs is a fixed array of pointers to Record's and num_recs is a size_t that holds the number of valid records. compar is this: int compar(const void *p1, const void *p2) { import core.stdc.string : strcmp; const Record **rp1 = cast(Record **)p1; const Record **rp2 = cast(Record **)p2; return strcmp((*rp1).name.ptr, (*rp2).name.ptr); } The error is: Error: function testd.compar (const(void*) p1, const(void*) p2) is not callable using argument types () I don't quite understand what those parentheses mean: is it implying "no arguments" and if so, where would one provide arguments? Joe Out of interest: I wonder what's your usecase for using qsort. Or in other words: why you can't use the high-level std.algorithm.sorting.sort?
Re: core.stdc.stdlib._compare_fp_t and qsort
On Monday, 12 March 2018 at 02:44:17 UTC, Joe wrote: I saw the extern(C) and I believe I tried it before my previous post, but dismissed it because I saw no difference in compiler behavior. Yeah, the compiler should just tell you what specifically it is complaining about instead of making you hunt. These "no function match" errors are actually my #1 pain point as a daily D user... and as a regular D tech support rep, I see there are very frequent problems for everyone else too. Could you explain or direct me to something that elucidates why the "scope" qualifiers are needed? And is there something else that is necessary inside compar() or are the "scope"s just for decorative purposes? So `scope` means you promise not to escape the reference outside your scope; that you won't store the pointers somewhere else for future use. It is defined here: https://dlang.org/spec/function.html#parameters tho that's pretty light. The compiler only checks it if you throw some random -dipsomething switch. The idea is something similar to Rust's borrow checker. It aims to make it a compile error to sneak out pointers to temporaries through our callback. You wouldn't do that anyway with a compare function, but now it is an error to try ( if the strict enforcement thing is thrown. and i don't remember the name, they are all random dip numbers on the switches) I'm actually mildly against adding these directly to C functions, but this attribute spam is being added to all the druntime definitions. It requires stuff like nothrow and nogc on a bunch of handlers too, ugh, but the idea behind it is to make sure you don't break the C function's documented rules, but here checked by the D compiler without needing any runtime wrappers/checks. But until recently it wasn't checked at all which is why I wasn't sure if that was causing your error. I guess it is required to match the function call even if the strict enforcement isn't thrown now.
Re: core.stdc.stdlib._compare_fp_t and qsort
On Monday, 12 March 2018 at 03:13:08 UTC, Seb wrote: Out of interest: I wonder what's your usecase for using qsort. Or in other words: why you can't use the high-level std.algorithm.sorting.sort? This is only temporary. I will be using std.algorithm.sorting.sort. I was converting a C program and it annoyed me that I couldn't get the qsort invocation past the D compiler.
Re: How do you call an eponymous template that has a secondary template arg?
On Sunday, 11 March 2018 at 12:05:56 UTC, aliak wrote: * aliasOf!int!"string" // multiple ! arguments are not allowed * (aliasOf!int)!"string" // error c-style cast * aliasOf!int.aliasOf!"string" // template isAliasOf(alias a) does not have property 'isAliasOf Yeah, that's a little hole in the grammar, but there are ways: // Declare an alias: alias aliasOfInt = aliasOf!int; // And use that: assert(!aliasOfInt!string); Or use std.meta.Instantiate: assert(!Instantiate!(aliasOf!int, string)); -- Simen