Re: How can I tell D that function args are @nogc etc.

2024-04-10 Thread John Dougan via Digitalmars-d-learn

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?

2024-04-10 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-04-10 Thread Andy Valencia via Digitalmars-d-learn
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?

2024-04-10 Thread Lettever via Digitalmars-d-learn

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?

2024-04-10 Thread Andy Valencia via Digitalmars-d-learn

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..

2024-04-10 Thread Richard (Rikki) Andrew Cattermole via Digitalmars-d-learn

```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..

2024-04-10 Thread WhatMeWorry via Digitalmars-d-learn



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

2024-04-10 Thread Steven Schveighoffer via Digitalmars-d-learn
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.

2024-04-10 Thread Steven Schveighoffer via Digitalmars-d-learn
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.

2024-04-10 Thread Richard (Rikki) Andrew Cattermole via Digitalmars-d-learn
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

2024-04-10 Thread Richard (Rikki) Andrew Cattermole via Digitalmars-d-learn

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.