Re: How can I tell D that function args are @nogc etc.
Interesting. Thank you to both of you. On Wednesday, 10 April 2024 at 17:38:21 UTC, Steven Schveighoffer wrote: On Wednesday, 10 April 2024 at 11:34:06 UTC, Richard (Rikki) Andrew Cattermole wrote: Place your attributes on the right hand side of the function, not the left side. Use the left side for attributes/type qualifiers that go on the return type. Just a word of warning, this explanation suggests putting qualifiers on the left side would affect the return type, this is not the case. So in my example, what did I actually tell the compiler with the placement of the attributes? And how was it different between the function type alias declaration, and the actual function declaration? More specifically, what are the semantic differences below? ```d alias FnPrefixT = @nogc nothrow @safe bool function(int); // Versus alias FnSuffixT = bool function(int) @nogc nothrow @safe; ``` and ```d @nogc nothrow @safe bool fnPrefix(int) { stuff } // Versus bool fnSuffix(int) @nogc nothrow @safe { stuff } ``` Is there a reasonably clear overview of how this works anywhere? What I have seen so far led me to the vague impression that it wasn't significant just like attribute ordering. -- john
Re: Why does Nullable implicitly casts when assigning a variable but not when returning from a function?
On Wednesday, April 10, 2024 2:41:56 PM MDT Lettever via Digitalmars-d-learn wrote: > ``` > import std; > > Nullable!int func() => 3; > void main() { > Nullable!int a = 3; > //works fine > Nullable!int b = func(); > //does not compile > } Technically, no implicit conversion is going on here. What's happening is that you're effectively using an alternate syntax for construction. So, Nullable!int a = 3; gets treated as Nullable!int a = Nullable!int(3); And this only happens in cases where the compiler must do construction, which is what you're doing when you declare a variable and give it a value that isn't the exact same type as the variable, since construction is the only thing that it can do in that case. It's either that or requiring that you call the constructor explicitly, and for better or worse, the compiler interprets it as a constructor call without the explicit constructor call. But it only works, because it's explicit that a variable is being declared, and therefore it must be the case that you're constructing it when you use = to give it a value. On the other hand, when you're returning a value from a function, you're not telling the compiler to construct anything, because unlike when declaring a variable, construction isn't necessary. So, at that point, the value being returned has to either have the same type as the return type, or it needs to implicitly convert to the return type, and int does not implicitly convert to Nullable!int, so you get an error. The only implicit conversions that the language has for user-defined types are 1. When a type has an alias this, it defines an implicit conversion _to_ the type that the alias evaluates to. 2. When a type has a base type (e.g. a class or an enum), then it will implicitly convert to its base type. There is no way to implicitly convert via construction, since having that makes it harder to keep track of what happens when calling a function. This can get very bad in C++ given that it allows you to declare a type to implicitly convert to, and it has implicit construction. They had to add the ability to mark parameters as explicit to block implicit conversions, because it causes enough problems. So, Walter decided to simplify how implicit conversions work in D, and we don't have implicit construction. Of course, there are times when the lack of implicit construction can get annoying (and Nullable is a prime example of that), but other problems are reduced as a result. For Nullable, the solution is to use the nullable helper function so that you don't have to repeat the type. E.G. Nullable!int func() => nullable(3); or auto func() => nullable(3); And in many cases, rather than implicitly calling the constructor when declaring a variable, it's arguably better to just use auto with an explicit constructor call (and it definitely helps with Nullable). E.g. auto a = nullable(3); allows you to not bother giving the explicit type of the Nullable at all. - Jonathan M Davis
mmap file performance
I wrote a "count newlines" based on mapped files. It used about twice the CPU of the version which just read 1 meg at a time. I thought something was amiss (needless slice indirection or something), so I wrote the code in C. It had the same CPU usage as the D version. So...mapped files, not so much. Not D's fault. And writing it in C made me realize how much easier it is to code in D! The D version: import std.stdio : writeln; import std.mmfile : MmFile; const uint CHUNKSZ = 65536; size_t countnl(ref shared char[] data) { size_t res = 0; foreach (c; data) { if (c == '\n') { res += 1; } } return res; } void usage(in string progname) { import core.stdc.stdlib : exit; import std.stdio : stderr; stderr.writeln("Usage is: ", progname, " %s ..."); exit(1); } public: void main(string[] argv) { if (argv.length < 2) { usage(argv[0]); } foreach(mn; argv[1 .. $]) { auto mf = new MmFile(mn); auto data = cast(shared char[])mf.opSlice(); size_t res; res = countnl(data); writeln(mn, ": ", res); } } And the C one (no performance gain over D): #include #include #include #include #include static unsigned long countnl(int fd, char *nm) { char *buf, *p; struct stat st; unsigned int cnt; unsigned long res; if (fstat(fd, ) < 0) { perror(nm); return(0); } cnt = st.st_size; buf = mmap(0, cnt, PROT_READ, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { perror(nm); return(0); } res = 0L; for (p = buf; cnt; cnt -= 1) { if (*p++ == '\n') { res += 1L; } } munmap(buf, st.st_size); return(res); } int main(int argc, char **argv) { int x; for (x = 1; x < argc; ++x) { unsigned long res; char *nm = argv[x]; int fd = open(nm, O_RDONLY); if (fd < 0) { perror(nm); continue; } res = countnl(fd, nm); close(fd); printf("%s: %uld\n", nm, res); } }
Re: Why does Nullable implicitly casts when assigning a variable but not when returning from a function?
On Wednesday, 10 April 2024 at 21:38:22 UTC, Andy Valencia wrote: On Wednesday, 10 April 2024 at 20:41:56 UTC, Lettever wrote: ``` import std; Nullable!int func() => 3; void main() { Nullable!int a = 3; //works fine Nullable!int b = func(); //does not compile } Why make func() Nullable? It just wants to give you an int, right? Making it a function returning an int fixes this. Andy Its an example
Re: Why does Nullable implicitly casts when assigning a variable but not when returning from a function?
On Wednesday, 10 April 2024 at 20:41:56 UTC, Lettever wrote: ``` import std; Nullable!int func() => 3; void main() { Nullable!int a = 3; //works fine Nullable!int b = func(); //does not compile } Why make func() Nullable? It just wants to give you an int, right? Making it a function returning an int fixes this. Andy
Re: What I thought was straightforward blew up in my face..
```c void SDL_GetVersion(SDL_version * ver); ``` It doesn't return anything. Its return type is void. See the argument list where it lists the types of the arguments: ``template writeln is not callable using argument types !()(string, void)`` Which aligns with the arguments you passed to ``writeln``.
What I thought was straightforward blew up in my face..
import bindbc.sdl; import bindbc.loader; SDL_version ver; SDL_GetVersion(); writeln("version = ", ver); // runs and displays: version = SDL_version(2, 30, 2) writeln("version = ", SDL_GetVersion()); // compile fails with // template `writeln` is not callable using argument types `!()(string, void)` // C:\D\dmd2\windows\bin64\..\..\src\phobos\std\stdio.d(4249,6): Candidate is: `writeln(T...)(T args)` // Error C:\D\dmd2\windows\bin64\dmd.exe failed with exit code 1. I'll confess I can't make heads or tails out of the error messages. Is this impossible or is there some way to make writeln happy?
Re: "Error: `TypeInfo` cannot be used with -betterC" on a CTFE function
On Tuesday, 9 April 2024 at 23:50:36 UTC, Richard (Rikki) Andrew Cattermole wrote: On 10/04/2024 11:21 AM, Liam McGillivray wrote: On Sunday, 7 April 2024 at 08:59:55 UTC, Richard (Rikki) Andrew Cattermole wrote: Unfortunately runtime and CTFE are the same target in the compiler. So that function is being used for both, and hence uses GC (appending). Are you sure that string appending was really the problem that caused the "TypeInfo" build error? I forgot about this, but I had already had a working CTFE function with string appending before adding the new one that lead to this error. The symbols that it generates could be used in the program compiled with `betterC`. No, for a string it shouldn't trigger the need for TypeInfo. But that wouldn't have worked regardless. Array appending calls a runtime function which accepts `TypeInfo` (In this case, `TypeInfoGeneric!char`). So this does indeed involve `TypeInfo`. But also, even if `TypeInfo` weren't involved, it also needs the GC which is unavailable with betterC. It's just that the `TypeInfo` error happens first. The move to use templates instead of `TypeInfo` is slowly happening. -Steve
Re: How can I tell D that function args are @nogc etc.
On Wednesday, 10 April 2024 at 11:34:06 UTC, Richard (Rikki) Andrew Cattermole wrote: Place your attributes on the right hand side of the function, not the left side. Use the left side for attributes/type qualifiers that go on the return type. Just a word of warning, this explanation suggests putting qualifiers on the left side would affect the return type, this is not the case. Attributes apply to the *declaration*. In some cases this effectively applies to the return type, in some cases it applies to the function, in some cases it applies to the context pointer. In order to apply type constructors to the return type, you need to use parentheses: ```d const int foo(); // const applies to the context pointer of `foo`, not `int` const(int) bar(); // const applies to `int` return type ref int baz(); // ref applies to `baz`, which in turn means "ref returning function" ``` Where this becomes tricky is return types that are function pointers/delegates. Then using the right side of the function/delegate *type* is the only way. ```d @safe void function() foo(); // `foo` is safe, the function pointer it returns is not void function() @safe bar(); // `bar` is not safe, the function pointer returned is void function() @safe baz() @safe; // both are safe ``` -Steve
Re: How can I tell D that function args are @nogc etc.
Place your attributes on the right hand side of the function, not the left side. Use the left side for attributes/type qualifiers that go on the return type. ```d bool[7] stagesToProcess = false; bool shouldDoInStages(int index) @nogc nothrow @safe { return stagesToProcess[index]; } bool shouldDoNever(int index) @nogc nothrow @safe { return false; } template processSafely(int low, int high) { alias ShouldDoFnT = bool function(int) @nogc nothrow @safe; uint processSafely(ShouldDoFnT shouldDo) @nogc nothrow @safe { assert(low < high); uint stepsProcessed; for (int ii = low; ii <= high; ii++) { if (shouldDo(ii)) { stepsProcessed++; } } return stepsProcessed; } } void main() { stagesToProcess = [false, false, true, true, false, true, false]; uint count = processSafely!(1, 4)(); assert(count == 2); } ```
Re: "Error: `TypeInfo` cannot be used with -betterC" on a CTFE function
On 10/04/2024 2:50 PM, Liam McGillivray wrote: On Tuesday, 9 April 2024 at 23:50:36 UTC, Richard (Rikki) Andrew Cattermole wrote: The string mixin triggers CTFE, if ``EnumPrefixes`` wasn't templated, that would cause codegen and hence error. If you called it in a context that wasn't CTFE only, it would codegen even with template and would error. For quite a long time we emitted -betterC errors during semantic, we learned that this was all around a bad idea and moved (hopefully all but I doubt it) into the glue code. So only if it codegens will it error. Well then, perhaps this is a bug (though a useful bug in my case). Not a bug, this took us quite a while to get reliable.