www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is it possible to return mutable and const range from a single method?

reply realhet <real_het hotmail.com> writes:
Hello,

I managed to make a universal getParent() function which can 
preserve constness.
I also had success with inout functions that work with this inout 
getParent method.
Is it possible to do something like this but for the allParents 
input range producer method?
In the const range implementation, the internal storage is 
non-const, it is just implicitly converted to const in the 
front() property.
It gives the protection I was needed but is it possible to make 
this prettier?


```d
import std;

class A{
     inout(A) getParent() inout{ return null; }

     this(A p){
     }
}

class B : A{
     A parent;

     override inout(A) getParent() inout{ return parent; }

     auto allParents(){
       struct ParentRange{
         A act;
          property bool empty() const{ return act is null; }
          property A front() { return act; }
         void popFront(){ act = act.getParent; }
       }
       return ParentRange(getParent);
     }

     auto allParents()const {
       struct ConstParentRange{
         A act;
          property bool empty() const{ return act is null; }
          property const(A) front() inout { return act; }
         void popFront(){ act = act.getParent; }
       }
       return ConstParentRange(cast()getParent);
     }

     this(A p){
         super(p);
         parent = p;
     }
}

auto test(inout A a, void delegate(inout A) fun){
     auto p = a.getParent;
     fun(p);
}

void main()
{
     auto a = new A(null);
     auto b = new B(a);

     const c = b;

     writeln(c.getParent);
     c.test((in d){ writeln(d); });

     writeln;
     c.allParents.each!writeln;
     writeln;
     b.allParents.each!writeln;

}
```
Aug 22 2022
next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 8/22/22 09:36, realhet wrote:

 It gives the protection I was needed but is it possible to make this
 prettier?
      auto allParents(){
        struct ParentRange{
          A act;
           property bool empty() const{ return act is null; }
           property A front() { return act; }
          void popFront(){ act = act.getParent; }
        }
        return ParentRange(getParent);
      }

      auto allParents()const {
        struct ConstParentRange{
          A act;
           property bool empty() const{ return act is null; }
           property const(A) front() inout { return act; }
          void popFront(){ act = act.getParent; }
        }
        return ConstParentRange(cast()getParent);
      }
In other words, is it possible to combine the double implementation above? The following looks at typeof(this) to pick the type. My test asserts seem to pass: import std; class A{ inout(A) getParent() inout{ return null; } void nonConstMemberFunc() {} void constMemberFunc() const {} this(A p){ } } class B : A{ A parent; override inout(A) getParent() inout{ return parent; } struct ConstOrMutable(T) { // This alias is not strictly necessary; 'static if' // could define 'act' instead. static if (is (T == const)) { alias X = const(A); } else { alias X = A; } X act; property auto empty() const { return act is null; } property auto front() { return act; } void popFront() { act = act.getParent; } } auto allParents() inout { return ConstOrMutable!(typeof(this))(cast()getParent()); } this(A p){ super(p); parent = p; } } auto test(inout A a, void delegate(inout A) fun){ auto p = a.getParent; fun(p); } void main() { auto a = new A(null); auto b = new B(a); assert( __traits(compiles, b.constMemberFunc())); assert( __traits(compiles, b.nonConstMemberFunc())); const c = b; assert( __traits(compiles, c.constMemberFunc())); assert(!__traits(compiles, c.nonConstMemberFunc())); } Ali
Aug 22 2022
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/22/22 12:36 PM, realhet wrote:
 Hello,
 
 I managed to make a universal getParent() function which can preserve 
 constness.
 I also had success with inout functions that work with this inout 
 getParent method.
 Is it possible to do something like this but for the allParents input 
 range producer method?
 In the const range implementation, the internal storage is non-const, it 
 is just implicitly converted to const in the front() property.
 It gives the protection I was needed but is it possible to make this 
 prettier?
Technically your code is a reasonable compromise. The two versions are different enough that it might not make sense to combine them. It is possible to write the same function for both const and mutable overloads by using the `this` template parameter: ```d import std.stdio; class C { void foo(this This)(string s) {writefln("for %s this is %s", s, This.stringof); } } void main() { auto c = new C; c.foo("mutable"); const c2 = c; c2.foo("const"); } ``` But your common code is going to have a few static ifs in it, simply because const class instances are not assignable. You might find some success with std.typecons.Rebindable. -Steve
Aug 22 2022
parent realhet <real_het hotmail.com> writes:
On Monday, 22 August 2022 at 19:35:11 UTC, Steven Schveighoffer 
wrote:
 It is possible to write the same function for both const and 
 mutable overloads by using the `this` template parameter:
I guess before the "inout", "this This" was the only way to do this. I must remember this, it's really usefull. I also had lack of knowledge about that is(T==const) expression that Ali mentioned. In my program, I passed isConst as a template parameter with default initialization. It only needed one static if, where the range returns the front. Thank You all! - descendant classes can have parent or not (overload getParent) - allParents, thisAndAllParents: optionally enumerates this before the parents. - it can also filter the class type allParents!CustomContainer - supports const. It's much better than the previous visitor_function_with_a_callback_function_that_returns_false_w en_it_wants_to_stop thing. I really beleive that in the end it generates only a while loop that the compiler can optimize well. (Unlike recursion or callbacks) ```d inout(Container) getParent() inout { return null; } void setParent(Container p){} auto thisAndAllParents(Base : Cell = Cell, bool thisToo = true, bool isConst=is(typeof(this)==const))() inout{ struct ParentRange{ private Cell act; private void skip(){ static if(is(Base==Cell)) {} else while(!empty && (cast(Base)act is null)) popFront; } this(const Cell a){ act = cast()a; skip; } property bool empty() const{ return act is null; } void popFront(){ act = act.getParent; skip; } auto front() { static if(isConst) return cast(const Base)act; else return cast( Base)act; } } return ParentRange(thisToo ? this : getParent); } auto allParents(Base : Cell = Container)() inout{ return thisAndAllParents!(Base, false); } ```
Aug 22 2022