inout template parameter, or a solution to the templated container issue

Steven Schveighoffer schveiguy at yahoo.com
Wed Jun 12 08:33:12 PDT 2013


On Wed, 12 Jun 2013 02:08:48 -0400, deadalnix <deadalnix at gmail.com> wrote:

> We currently have a problem with containers : it is very difficult to  
> implement them in a way that is compliant type qualifiers. To restate  
> the problem shortly, let's imagine we want to implement array's as a  
> library type.
>
> struct Array(T) {
>      size_t length;
>      T* ptr;
>      // Methods
> }
>
> Now we have several problems with type qualifiers. For instance, Array!T  
> won't implicitly cast to Array!const(T), and const(Array!T) now become a  
> completely useless type, as ptr will become const by transitivity which  
> will create internal inconsistencies.
>
> This problem is known and makes it hard to provide a nice container  
> library for D. This also imply a lot of circuitry in the compiler that  
> can (and should) be provided as library.
>
> So I propose to introduce the inout template parameter type. We declare  
> as follow :
>
> Array(inout T) {
>      size_t length;
>      T* ptr
> }
>
> The inout template parameter is a type parameter, and so don't overload  
> on them (if both Array(T) and Array(inout T) the instantiation is  
> ambiguous).
>
> Within the template, T is always seen as inout, and only one  
> instantiation occur for all top type qualifiers. Array!T and  
> Array!const(T) refers to the same instance of Array. As a result, this  
> makes it impossible to specialize Array on T's type qualifier.

The one problem I see here is the case where you want to have Array mutate  
its data.

For example, let's say Array makes ptr and length private overloads  
opIndex AND opIndexAssign to prevent taking the the address of its data.

How do you do opIndexAssign with your mechanism?  There is no "mutable"  
type qualifier, so there isn't a way to say, "only allow calling this  
function if T is mutable," like we have with const and immutable member  
functions.

Another issue here is, what if you don't want Array to be a template?   
That is, you want:

struct IntArray {
     size_t length;
     int *ptr;
}

How do you make this tail-const-able?

> The real type qualifier is determined from the outside. A user of  
> Array!T consider inout as meaning mutable, when a user of Array!const(T)  
> consider it as meaning const, for anything related to Array.
>
> Implicit cast is allowed for instances of Array with different inout  
> parameter's type qualifier, as long as implicit conversion between such  
> qualifier is allowed. Array!T implicitly convert to Array!const(T) but  
> not the other way around.
>
> Finally, Array's type qualifier turtle down to inout parameters's type  
> qualifier. alias A = Array!T; static assert(is(const(A)) ==  
> const(Array!const(T))); alias B = Array!immutable(T); static  
> assert(is(const(A)) == const(Array!immutable(T)));
>
> The idea popped in my mind yesterday, so it is not really super fleshed  
> out, and I'm not sure if some horrible dark corner case makes it  
> completely worthless. But it seems super promising to me, so I want to  
> share. The current situation isn't satisfying and we desperately need a  
> solution.

It's a very good start, and very close to the solution I have.  I'm going  
to finish my article and post it hopefully next week.

-Steve


More information about the Digitalmars-d mailing list