September 01, 2021

On Wednesday, 1 September 2021 at 12:32:36 UTC, Steven Schveighoffer wrote:

> >

With the implicit opCast/constructor approach (or the opHeadMutable approach), the compiler doesn't have to understand the relationships. It just needs to know the original type and the desired type, and call the appropriate user-defined function to perform the conversion.

It just means you need to handle all the implicit conversions, which is not trivial. The compiler already has rules for implicit const conversions, some of which have subtle pitfalls if you get them wrong. I'd rather utilize those mechanisms rather than relying on each type author to get them all right.

As long as the type author does not use unsafe casts, the compiler will prevent them from getting the conversions wrong.

September 01, 2021

On Wednesday, 1 September 2021 at 11:04:41 UTC, Steven Schveighoffer wrote:

>

It's possible to forward, i.e.:

template Vector(T) if (isConst!T) { alias Vector = tailconst(.Vector!(Unqual!T)); }

If you look at arrays, const(T)[] is the same as tailconst(T[]). This would be similar.

The problem I have with any other approach is that the compiler doesn't understand the relationships, and you will run into weird corner cases. It can't correctly add or strip the qualifier to the correct members when calling compatible functions.

-Steve

If the type is unrelated, then you are back to the heuristic problem.

September 01, 2021

On Wednesday, 1 September 2021 at 11:57:01 UTC, Paul Backus wrote:

>

With the implicit opCast/constructor approach (or the opHeadMutable approach), the compiler doesn't have to understand the relationships. It just needs to know the original type and the desired type, and call the appropriate user-defined function to perform the conversion.

It still wouldn't work when manipulating things by reference.

In fact, the litmus test for a solution to that problem should be: can we reproduce the behavior of builtin types such as T* or T[] with it?

September 01, 2021

On Wednesday, 1 September 2021 at 12:52:47 UTC, deadalnix wrote:

>

On Wednesday, 1 September 2021 at 11:57:01 UTC, Paul Backus wrote:

>

With the implicit opCast/constructor approach (or the opHeadMutable approach), the compiler doesn't have to understand the relationships. It just needs to know the original type and the desired type, and call the appropriate user-defined function to perform the conversion.

It still wouldn't work when manipulating things by reference.

In fact, the litmus test for a solution to that problem should be: can we reproduce the behavior of builtin types such as T* or T[] with it?

It doesn't work when manipulating built-in types by reference either:

void manipulate(ref const(int)[] arr) {}

void main()
{
    const(int[]) arr;
    manipulate(arr);
    // Error: cannot pass argument `arr` of type `const(int[])`
    // to parameter `ref const(int)[] arr`
}
September 01, 2021

On Tuesday, 31 August 2021 at 17:21:02 UTC, deadalnix wrote:

>

...
For instance, if we have a Vector!T, then we want it to convert to a Vector!(const T) and we want const(Vector!T) == const(Vector!(const T)), all of it implicitly. Same goes for other qualifiers.

To put it bluntly, I have no idea how to achieve this. And I don't think we'll have a good set of collection before this is sorted out.
...

Try this template parameter:

import std.traits : CopyConstness, isMutable;


void main(){
    {
        Vector!int a;
        Vector!(const int) b = a;
        const Vector!int c = b;
    }
    {
        Vector!int a;
        Vector!(const int) b;
        b = a;
    	
    }
}


struct Vector(T){
    alias ElementType = T;

    this(U, this This)(ref Vector!U rhs)
    if(!is(U == T) && isCopyable!(This, typeof(rhs))){
    	//impl
    }

    this(U, this This)(Vector!U rhs)
    if(isCopyable!(typeof(rhs), This)){
    	//impl
    }

	///TODO copy ctors...

    void opAssign(U, this This)(auto ref Vector!U rhs)
    if(isCopyable!(typeof(rhs), This) && isMutable!This){
    	//impl
    }
}

private enum bool isCopyable(From, To) = is(
	CopyConstness!(From, From.ElementType[]) : CopyConstness!(To, To.ElementType[])
);

September 01, 2021

On Wednesday, 1 September 2021 at 13:22:46 UTC, Paul Backus wrote:

>

It doesn't work when manipulating built-in types by reference either:

void manipulate(ref const(int)[] arr) {}

void main()
{
    const(int[]) arr;
    manipulate(arr);
    // Error: cannot pass argument `arr` of type `const(int[])`
    // to parameter `ref const(int)[] arr`
}

No you right, this is not what I meant (but this is def what I wrote).

I meant that int[][] or int[]* turtle the qualifier down.

September 01, 2021

On Wednesday, 1 September 2021 at 18:23:06 UTC, deadalnix wrote:

>

No you right, this is not what I meant (but this is def what I wrote).

I meant that int[][] or int[]* turtle the qualifier down.

Are you sure?

void manipulate(const(int)[][] arr) {}

void main()
{
    const(int[][]) arr;
    manipulate(arr);
    // Error: cannot pass argument `arr` of type `const(int[][])`
    // to parameter `const(int)[][] arr`
}
September 01, 2021

On Wednesday, 1 September 2021 at 18:32:29 UTC, Paul Backus wrote:

>

On Wednesday, 1 September 2021 at 18:23:06 UTC, deadalnix wrote:

>

No you right, this is not what I meant (but this is def what I wrote).

I meant that int[][] or int[]* turtle the qualifier down.

Are you sure?

void manipulate(const(int)[][] arr) {}

void main()
{
    const(int[][]) arr;
    manipulate(arr);
    // Error: cannot pass argument `arr` of type `const(int[][])`
    // to parameter `const(int)[][] arr`
}

Yes, const(int[][]) is effectively const(const(const(int)[])[]).

In the same way, you want const(Vector!int[]) to be const(const(Vector!(const(int))[]).

Which means that you should be able to convert implicitly a reference to a Vector!int into a reference to a Vector!(const(int)).

In your example, you remove const qualifiers, so it obviously doesn't work. But it work in the other direction, when you add them. It won't for a collection even with an implicit cast operation, as what is really desired is some kind of reinterpret_cast.

September 01, 2021

On Wednesday, 1 September 2021 at 02:08:40 UTC, max haughton wrote:

>

We need better containers to compete with other languages. On top of that, there are patterns that other languages cannot do. One thing that comes to mind is how D is one of not many languages (languages with macros or similar probably can), that can easily allow one to write containers which lie about their layout. The most profitable example of that I can think of is automatically switching arrays of POD types to be a struct of arrays internally. This transformation makes random access to elements slower, however (say) loops where access to a subset of the elements dominates then this is much faster due to improved cache efficiency.

Great point. Game engines extensively use such data layouts when storing vertex data. That's reason for Jai wanting a builtin SoA type. Jai has interestingly since made that a library feature.

September 01, 2021

On Wednesday, 1 September 2021 at 00:52:04 UTC, deadalnix wrote:

>

You seem to be confused. The C++11 standard prohibits std::string from using COW. Existing C++ implementations were using COW, and getting out of it was what was a disaster (notably, there were breaking ABI changes, which the C++ ecosystem is not well suited to handle).

In what ways was it a disaster? Because most strings are small enough to benefit from the small string optimization?