www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Qualifier parameters (inout on steroids)

reply Tomer Filiba <tomerfiliba gmail.com> writes:
`inout` is a useful feature, but it's nearly impossible to 
actually use it. Suppose I have

struct MyTable {
      property items() inout {
         return Range(&this);
     }

     static struct Range {
         MyTable* table; // can't be inout
     }
}

I want my items-range to be const if `this` is const or 
modifiable if `this` is modifiable. I ended up with

      property items() {
         return Range!false(&this);
     }
      property items() const {
         return Range!true(&this);
     }

     static struct Range(bool isConst) {
         static if (isConst) {
             const(MyTable)* table;
         } else {
             MyTable* table;
         }
     }

Also, I can't define the struct inside the `items` method since 
it's used in two other methods (`keys` and `values`), only they 
hold a different `front`

It would be really nice if I could "capture" the qualifiers and 
be parameterized on them as well. For example,

int opApply(qual Q)(scope int delegate(int x) Q dg) Q;

Meaning, my opApply is Q if dg is Q. In other words, I'm 
`nothrow` if dg is `nothrow`, I'm ` nogc` if dg is ` nogc`, I'm 
`pure` if dg is `pure`, etc. Inout is just a special case of this 
feature, e.g.

int opApply(qual Q)(scope int delegate(Q int x) dg) Q;

E.g., I'm const if dg takes a const first parameter. So using my 
first example,

      property items(qual Q)() Q {
         return Range!Q(&this);
     }

     static struct Range(qual Q) {
         Q(MyTable)* table;
     }

Much more elegant and concise. It needn't generate different 
object code for instantiations of course, it only needs to be 
respected by the frontend and thrown away later.


-tomer
Sep 29 2016
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 9/29/16 8:53 AM, Tomer Filiba wrote:
 `inout` is a useful feature, but it's nearly impossible to actually use
 it. Suppose I have

 struct MyTable {
      property items() inout {
         return Range(&this);
     }

     static struct Range {
         MyTable* table; // can't be inout
     }
 }

 I want my items-range to be const if `this` is const or modifiable if
 `this` is modifiable.
Yes, this is a problem -- but not with inout. This is a problem with the lack of tail modifier syntax. Observe how this works if Range is really a dynamic array: property inout(T)[] items() inout { return theItems; // a dynamic array } Works just fine. What we need is a way to say tailinout(Range), which means that the table member is mutable, but points at inout data.
 I ended up with

      property items() {
         return Range!false(&this);
     }
      property items() const {
         return Range!true(&this);
     }
There is a way to make this more DRY, but it is still ugly, and loses the benefits of inout. You need to use template this parameters: struct Range(T) { T *table; } property items(this T)() { return Range!T(&this); }
 It would be really nice if I could "capture" the qualifiers and be
 parameterized on them as well. For example,

 int opApply(qual Q)(scope int delegate(int x) Q dg) Q;
I once thought this would work well, but in practice I think it would be a disaster. Imagine everyone defining their own set of qualifiers and how they will work. It's also a template, and does not protect against modification of mutable data, or static if to change layout/implementation/etc. I am working on an article to propose a tail modifier syntax. This would solve most of these problems. -Steve
Sep 29 2016