www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Supporting and signature-checking all foreach variations

reply Ashish Myles <marcianx gmail.com> writes:
I want to define a general-purpose centroid computer for point containers
and ran into a couple of challenges. Firstly, here is the basic code

    Point3 computeCentroid(PointContainer)(const ref PointContainer C)
        if (...)    // want a signature constraint for usability of foreach
    {
        Point3 c = Point3(0.0, 0.0, 0.0);
        size_t total = 0;
        foreach(Point3 p; C) {   // enforce that the container supports this
            c += p; ++total;
        }
        if (total > 0)
            c /= cast(double)(total);
        return c;
    }

I want to have the most generally-applicable version of this functionality
(for const/immutable/etc containers supporting foreach in various ways),
ideally without needing to write multiple versions of this function.

1. Since support for foreach can be added in many ways (with
  ref/non-ref/const variants), I wanted to check if there was any
  signature constraint that could check if the container supports foreach
  as above. I looked into the "compiles" traits, but that doesn't work for
  statements.

  For an opAssign version, I had tried
    if (is(typeof(C.opApply(delegate(const ref Point3) { return 1;}))))
  but this is unelegant because the container's opApply could have instead
  supplied delegate(Point3) or delegate(ref Point3) (although the latter
  would require me to not use a "const" on the parameter declaration).

2. Secondly, TDPL on page 381 says that foreach iterates over C[], if
  C defines the opSlice() function without any arguments.
  However the code above doesn't seem to work and requires me to
  explicitly invoke the slice operator myself like
    foreach(p; C[]) { ... }
  when my data structure clearly defines the following functions.
    Point3[] opSlice() { return _cpts[]; }
    const (Point3)[] opSlice() const { return _cpts[]; }
  Is this a misunderstanding on my part or an unimplemented feature?

3. A more general question: Is there any by any chance a way to avoid the
  redundancy above of defining two opSlice() functions (or two opAssign()
  functions if I went that route -- one for const and another for ref)?
  I suspect that the answer is no, but I just wanted to verify.
Feb 25 2012
next sibling parent =?ISO-8859-1?Q?Alex_R=F8nne_Petersen?= <xtzgzorex gmail.com> writes:
On 25-02-2012 17:25, Ashish Myles wrote:
 I want to define a general-purpose centroid computer for point containers
 and ran into a couple of challenges. Firstly, here is the basic code

      Point3 computeCentroid(PointContainer)(const ref PointContainer C)
          if (...)    // want a signature constraint for usability of foreach
      {
          Point3 c = Point3(0.0, 0.0, 0.0);
          size_t total = 0;
          foreach(Point3 p; C) {   // enforce that the container supports this
              c += p; ++total;
          }
          if (total>  0)
              c /= cast(double)(total);
          return c;
      }

 I want to have the most generally-applicable version of this functionality
 (for const/immutable/etc containers supporting foreach in various ways),
 ideally without needing to write multiple versions of this function.

 1. Since support for foreach can be added in many ways (with
    ref/non-ref/const variants), I wanted to check if there was any
    signature constraint that could check if the container supports foreach
    as above. I looked into the "compiles" traits, but that doesn't work for
    statements.

    For an opAssign version, I had tried
      if (is(typeof(C.opApply(delegate(const ref Point3) { return 1;}))))
    but this is unelegant because the container's opApply could have instead
    supplied delegate(Point3) or delegate(ref Point3) (although the latter
    would require me to not use a "const" on the parameter declaration).

 2. Secondly, TDPL on page 381 says that foreach iterates over C[], if
    C defines the opSlice() function without any arguments.
    However the code above doesn't seem to work and requires me to
    explicitly invoke the slice operator myself like
      foreach(p; C[]) { ... }
    when my data structure clearly defines the following functions.
      Point3[] opSlice() { return _cpts[]; }
      const (Point3)[] opSlice() const { return _cpts[]; }
    Is this a misunderstanding on my part or an unimplemented feature?

 3. A more general question: Is there any by any chance a way to avoid the
    redundancy above of defining two opSlice() functions (or two opAssign()
    functions if I went that route -- one for const and another for ref)?
    I suspect that the answer is no, but I just wanted to verify.

I think std.traits.isIterable(T) is what you want. -- - Alex
Feb 25 2012
prev sibling next sibling parent Ashish Myles <marcianx gmail.com> writes:
On Sat, Feb 25, 2012 at 11:37 AM, Alex R=F8nne Petersen
<xtzgzorex gmail.com> wrote:
 On 25-02-2012 17:25, Ashish Myles wrote:
 1. Since support for foreach can be added in many ways (with
 =A0 ref/non-ref/const variants), I wanted to check if there was any
 =A0 signature constraint that could check if the container supports fore=


 =A0 as above. I looked into the "compiles" traits, but that doesn't work=


 =A0 statements.

I think std.traits.isIterable(T) is what you want.

Great! Now that I know the answer to the first question, it is so easy to find the answer on google :P . Technically, I want something like isIterable(T,Point3); given the definition of isIterable, I can easily get this functionality. I am still looking for answers to the second question (which might indicate a bug in the frontend) and third question whose answer would be really enlightening if what I requested is possible.
Feb 25 2012
prev sibling next sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 25.02.2012 20:25, Ashish Myles wrote:
 I want to define a general-purpose centroid computer for point containers
 and ran into a couple of challenges. Firstly, here is the basic code

      Point3 computeCentroid(PointContainer)(const ref PointContainer C)
          if (...)    // want a signature constraint for usability of foreach
      {
          Point3 c = Point3(0.0, 0.0, 0.0);
          size_t total = 0;
          foreach(Point3 p; C) {   // enforce that the container supports this
              c += p; ++total;
          }
          if (total>  0)
              c /= cast(double)(total);
          return c;
      }

 I want to have the most generally-applicable version of this functionality
 (for const/immutable/etc containers supporting foreach in various ways),
 ideally without needing to write multiple versions of this function.

 1. Since support for foreach can be added in many ways (with
    ref/non-ref/const variants), I wanted to check if there was any
    signature constraint that could check if the container supports foreach
    as above. I looked into the "compiles" traits, but that doesn't work for
    statements.

    For an opAssign version, I had tried
      if (is(typeof(C.opApply(delegate(const ref Point3) { return 1;}))))
    but this is unelegant because the container's opApply could have instead
    supplied delegate(Point3) or delegate(ref Point3) (although the latter
    would require me to not use a "const" on the parameter declaration).

 2. Secondly, TDPL on page 381 says that foreach iterates over C[], if
    C defines the opSlice() function without any arguments.
    However the code above doesn't seem to work and requires me to
    explicitly invoke the slice operator myself like
      foreach(p; C[]) { ... }
    when my data structure clearly defines the following functions.
      Point3[] opSlice() { return _cpts[]; }
      const (Point3)[] opSlice() const { return _cpts[]; }
    Is this a misunderstanding on my part or an unimplemented feature?

It's supposed to work. I think it's just not implemented yet.
 3. A more general question: Is there any by any chance a way to avoid the
    redundancy above of defining two opSlice() functions (or two opAssign()
    functions if I went that route -- one for const and another for ref)?
    I suspect that the answer is no, but I just wanted to verify.

-- Dmitry Olshansky
Feb 25 2012
prev sibling next sibling parent "Jesse Phillips" <jessekphillips+D gmail.com> writes:
On Saturday, 25 February 2012 at 16:26:05 UTC, Ashish Myles wrote:

 2. Secondly, TDPL on page 381 says that foreach iterates over 
 C[], if
   C defines the opSlice() function without any arguments.
   However the code above doesn't seem to work and requires me to
   explicitly invoke the slice operator myself like
     foreach(p; C[]) { ... }
   when my data structure clearly defines the following 
 functions.
     Point3[] opSlice() { return _cpts[]; }
     const (Point3)[] opSlice() const { return _cpts[]; }
   Is this a misunderstanding on my part or an unimplemented 
 feature?

Is not implemented, not seeing a bug report http://d.puremagic.com/issues/
 3. A more general question: Is there any by any chance a way to 
 avoid the
   redundancy above of defining two opSlice() functions (or two 
 opAssign()
   functions if I went that route -- one for const and another 
 for ref)?
   I suspect that the answer is no, but I just wanted to verify.

Not sure look into information on 'auto ref' and 'inout' these were built for removing duplications of const and ref functions. These have had some work done to make them functional, but could still have holes.
Feb 25 2012
prev sibling next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 02/25/2012 08:25 AM, Ashish Myles wrote:
 I want to define a general-purpose centroid computer for point containers
 and ran into a couple of challenges. Firstly, here is the basic code

      Point3 computeCentroid(PointContainer)(const ref PointContainer C)
          if (...)    // want a signature constraint for usability of 

      {
          Point3 c = Point3(0.0, 0.0, 0.0);
          size_t total = 0;
          foreach(Point3 p; C) {   // enforce that the container 

              c += p; ++total;
          }
          if (total>  0)
              c /= cast(double)(total);
          return c;
      }

...
 2. Secondly, TDPL on page 381 says that foreach iterates over C[], if
    C defines the opSlice() function without any arguments.

Although what you describe also seems useful, that heading seems to be about ranges and specifically about the three InputRange functions. The feature has indeed been implemented recently: http://d.puremagic.com/issues/show_bug.cgi?id=5605
    However the code above doesn't seem to work and requires me to
    explicitly invoke the slice operator myself like
      foreach(p; C[]) { ... }
    when my data structure clearly defines the following functions.
      Point3[] opSlice() { return _cpts[]; }
      const (Point3)[] opSlice() const { return _cpts[]; }
    Is this a misunderstanding on my part or an unimplemented feature?

But I've just verified that the following works with dmd 2.058: import std.stdio; struct Point3 {} struct MyCollection { Point3[] _cpts; Point3[] opSlice() { return _cpts; } // <-- _cpts[] works too const (Point3)[] opSlice() const { return _cpts; } } void main() { auto coll = MyCollection(); foreach (i; coll) { // ... } } Ali
Feb 26 2012
prev sibling parent Ashish Myles <marcianx gmail.com> writes:
On Sun, Feb 26, 2012 at 5:25 AM, Ali =C7ehreli <acehreli yahoo.com> wrote:
 On 02/25/2012 08:25 AM, Ashish Myles wrote:
 =A0 =A0However the code above doesn't seem to work and requires me to
 =A0 =A0explicitly invoke the slice operator myself like
 =A0 =A0 =A0foreach(p; C[]) { ... }
 =A0 =A0when my data structure clearly defines the following functions.
 =A0 =A0 =A0Point3[] opSlice() { return _cpts[]; }
 =A0 =A0 =A0const (Point3)[] opSlice() const { return _cpts[]; }
 =A0 =A0Is this a misunderstanding on my part or an unimplemented feature=


 But I've just verified that the following works with dmd 2.058:

 import std.stdio;

 struct Point3
 {}

 struct MyCollection
 {
 =A0 =A0Point3[] _cpts;

 =A0 =A0Point3[] opSlice() { return _cpts; } =A0// <-- _cpts[] works too

 =A0 =A0const (Point3)[] opSlice() const { return _cpts; }
 }

 void main()
 {
 =A0 =A0auto coll =3D MyCollection();

 =A0 =A0foreach (i; coll) {
 =A0 =A0 =A0 =A0// ...
 =A0 =A0}
 }

Oh good to know. I had downgraded my dmd after some new CTFE bugs resulted in compilation errors on some of my code, and hadn't thought to check with the latest version.
Feb 26 2012