www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - opCmp with and without const

reply berni44 <dlang d-ecke.de> writes:
In std.typecons, in Tuple there are two opCmp functions, that are 
almost identical; they only differ by one being const and the 
other not:

         int opCmp(R)(R rhs)
         if (areCompatibleTuples!(typeof(this), R, "<"))
         {
             static foreach (i; 0 .. Types.length)
             {
                 if (field[i] != rhs.field[i])
                 {
                     return field[i] < rhs.field[i] ? -1 : 1;
                 }
             }
             return 0;
         }

         int opCmp(R)(R rhs) const
         if (areCompatibleTuples!(typeof(this), R, "<"))
         {
             static foreach (i; 0 .. Types.length)
             {
                 if (field[i] != rhs.field[i])
                 {
                     return field[i] < rhs.field[i] ? -1 : 1;
                 }
             }
             return 0;
         }


What is the reason for having this? (I guess, that it's because 
the function may indirectly call opCmp of other types which may 
or may not be const.)

My real question is: Can this code duplication be avoided 
somehow? (I ask, because I've got a PR running, which increases 
the size of these functions and it doesn't feel good to have two 
long, almost identical functions.)
Dec 05 2019
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Friday, 6 December 2019 at 07:03:45 UTC, berni44 wrote:
 My real question is: Can this code duplication be avoided 
 somehow? (I ask, because I've got a PR running, which increases 
 the size of these functions and it doesn't feel good to have 
 two long, almost identical functions.)
You can use a template this parameter [1] to have a new copy of opCmp generated for each qualified version of Tuple it's called with: int opCmp(R, this This)(R rhs) const if (areCompatibleTuples!(typeof(this), R, "<")) { ... } This may lead to binary bloat, though, since you can potentially have separate instantiations for mutable, const, immutable, inout, shared, etc. [1] https://dlang.org/spec/template.html#template_this_parameter
Dec 05 2019
prev sibling next sibling parent Basile B. <b2.temp gmx.com> writes:
On Friday, 6 December 2019 at 07:03:45 UTC, berni44 wrote:
 In std.typecons, in Tuple there are two opCmp functions, that 
 are almost identical; they only differ by one being const and 
 the other not:

         int opCmp(R)(R rhs)
         if (areCompatibleTuples!(typeof(this), R, "<"))
         {
             static foreach (i; 0 .. Types.length)
             {
                 if (field[i] != rhs.field[i])
                 {
                     return field[i] < rhs.field[i] ? -1 : 1;
                 }
             }
             return 0;
         }

         int opCmp(R)(R rhs) const
         if (areCompatibleTuples!(typeof(this), R, "<"))
         {
             static foreach (i; 0 .. Types.length)
             {
                 if (field[i] != rhs.field[i])
                 {
                     return field[i] < rhs.field[i] ? -1 : 1;
                 }
             }
             return 0;
         }


 What is the reason for having this? (I guess, that it's because 
 the function may indirectly call opCmp of other types which may 
 or may not be const.)

 My real question is: Can this code duplication be avoided 
 somehow?
Usually `inout` is used. I'm pretty sure this is not possible here otherwise it would be done to avoid the dup.
 (I ask, because I've got a PR running, which increases the size 
 of these functions and it doesn't feel good to have two long, 
 almost identical functions.)
Well the content of body could be mixed in
Dec 06 2019
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Friday, December 6, 2019 12:03:45 AM MST berni44 via Digitalmars-d-learn 
wrote:
 In std.typecons, in Tuple there are two opCmp functions, that are
 almost identical; they only differ by one being const and the
 other not:

          int opCmp(R)(R rhs)
          if (areCompatibleTuples!(typeof(this), R, "<"))
          {
              static foreach (i; 0 .. Types.length)
              {
                  if (field[i] != rhs.field[i])
                  {
                      return field[i] < rhs.field[i] ? -1 : 1;
                  }
              }
              return 0;
          }

          int opCmp(R)(R rhs) const
          if (areCompatibleTuples!(typeof(this), R, "<"))
          {
              static foreach (i; 0 .. Types.length)
              {
                  if (field[i] != rhs.field[i])
                  {
                      return field[i] < rhs.field[i] ? -1 : 1;
                  }
              }
              return 0;
          }


 What is the reason for having this? (I guess, that it's because
 the function may indirectly call opCmp of other types which may
 or may not be const.)

 My real question is: Can this code duplication be avoided
 somehow? (I ask, because I've got a PR running, which increases
 the size of these functions and it doesn't feel good to have two
 long, almost identical functions.)
The issue is that there's no guarantee that the types being wrapped have a const opCmp. So, you can't just slap const or inout on Tuple's opCmp and have it work, but you do want it to be const if it can be const. So, two overloads are declared, and the template constraint takes care of checking whether that particular overload can be instantiated. A mixin could be used for the function bodies to avoid duplicating the internals, and it may be possible to use template this parameters as Paul Backus suggested (I'm not very familiar with template this parameters, so I don't know how well they'll work in this particular case), but ultimately, one way or another, you need to have a non-const opCmp declared for when the wrapped types don't have an opCmp that works with const and a const or inout opCmp for when the wrapped types do have an opCmp that works with const. - Jonathan M Davis
Dec 06 2019