220 11583 <93261ea6-195a-4838-8283-397a538e9444@isocpp.org> article
Path: news.gmane.org!not-for-mail
From: =?UTF-8?Q?=C5=81ukasz_Mendakiewicz?= <l.mendakiewicz@live.com>
Newsgroups: gmane.comp.lang.c++.isocpp.proposals
Subject: Re: Comments on n3976 (array_view)
Date: Thu, 26 Jun 2014 22:00:53 -0700 (PDT)
Lines: 927
Approved: news@gmane.org
Message-ID: <93261ea6-195a-4838-8283-397a538e9444@isocpp.org>
References: <ef9f1714-0b21-4ff5-87f3-ba3f4c8d06f4@isocpp.org>
 <CANh-dXnY3oNR6g2Jw7r+sT=dXn268OQ+TZ8uFcYzYcPBSWfGUw@mail.gmail.com>
 <81f95afc-7a67-45cd-950c-433528b2b8db@isocpp.org>
 <e5fe7212-7f50-4293-8251-9e49102a32ce@isocpp.org>
 <CAKJfoCE5FnF+8YCqfXvG+5zUWs5paZGmViujbwt4_rs2pdWD2Q@mail.gmail.com>
Reply-To: std-proposals@isocpp.org
NNTP-Posting-Host: plane.gmane.org
Mime-Version: 1.0
Content-Type: multipart/alternative; 
	boundary="----=_Part_372_32744078.1403845253991"
X-Trace: ger.gmane.org 1403845270 23778 80.91.229.3 (27 Jun 2014 05:01:10 GMT)
X-Complaints-To: usenet@ger.gmane.org
NNTP-Posting-Date: Fri, 27 Jun 2014 05:01:10 +0000 (UTC)
To: std-proposals@isocpp.org
Original-X-From: std-proposals+bncBC2NDJ47YMLBBB7VWOOQKGQEZ63OTJA@isocpp.org Fri Jun 27 07:01:03 2014
Return-path: <std-proposals+bncBC2NDJ47YMLBBB7VWOOQKGQEZ63OTJA@isocpp.org>
Envelope-to: gclcip-std-proposals@m.gmane.org
Original-Received: from mail-vc0-f197.google.com ([209.85.220.197])
	by plane.gmane.org with esmtp (Exim 4.69)
	(envelope-from <std-proposals+bncBC2NDJ47YMLBBB7VWOOQKGQEZ63OTJA@isocpp.org>)
	id 1X0OHR-0005xl-8T
	for gclcip-std-proposals@m.gmane.org; Fri, 27 Jun 2014 07:00:57 +0200
Original-Received: by mail-vc0-f197.google.com with SMTP id il7sf8510731vcb.0
        for <gclcip-std-proposals@m.gmane.org>; Thu, 26 Jun 2014 22:00:56 -0700 (PDT)
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20130820;
        h=x-gm-message-state:date:from:to:message-id:in-reply-to:references
         :subject:mime-version:x-original-sender:reply-to:precedence
         :mailing-list:list-id:list-post:list-help:list-archive
         :list-subscribe:list-unsubscribe:content-type;
        bh=96f+mu2kQnweB3TsxdoT13KCYZTVe2fqcB07vtN6SEI=;
        b=QDD+EAk2aIE5MnopBNU5R1NPBjNFnFElhFx3/+WeGv0SYrED8vCSobx3x3kRlkt5/2
         xlWYQZ/7miUgFbVimQhG2UJwpZxJqngcwSyUSTPSnqHDUr4bKdqqGlm71s9XdOI/TTns
         zIWcMWxXExAc1GO9bWH1VMoZm3UCg7NPXPLFkEAJtzyOv/zK5I0jyIi4ypzGKj+NiNJA
         e83M33UB/cqPfrdOhXUU+UWZkr5gOYPOooKvepF5VebjwHvcc3pzxIwvuBybjKCgFbzX
         mzRw0vajZRVT7vxOxqsujJASprXJ19NqIqVew5Hnt5eSML8s6MfzdncH7ORmyeVW/pYf
         RWHQ==
X-Gm-Message-State: ALoCoQnroytKHjbRh3YRmFkteqUzAMPAUWpDpl60rB3yoRldQHd1WtxyeUb+GJQWr7Z0N1VdzjNQ
X-Received: by 10.52.186.132 with SMTP id fk4mr9309639vdc.1.1403845256302;
        Thu, 26 Jun 2014 22:00:56 -0700 (PDT)
X-BeenThere: std-proposals@isocpp.org
Original-Received: by 10.50.153.15 with SMTP id vc15ls155486igb.42.gmail; Thu, 26 Jun
 2014 22:00:55 -0700 (PDT)
X-Received: by 10.50.79.137 with SMTP id j9mr206029igx.6.1403845255736;
        Thu, 26 Jun 2014 22:00:55 -0700 (PDT)
In-Reply-To: <CAKJfoCE5FnF+8YCqfXvG+5zUWs5paZGmViujbwt4_rs2pdWD2Q@mail.gmail.com>
X-Original-Sender: l.mendakiewicz@live.com
Precedence: list
Mailing-list: list std-proposals@isocpp.org; contact std-proposals+owners@isocpp.org
List-ID: <std-proposals.isocpp.org>
X-Google-Group-Id: 399137483710
List-Post: <http://groups.google.com/a/isocpp.org/group/std-proposals/post>, <mailto:std-proposals@isocpp.org>
List-Help: <http://support.google.com/a/isocpp.org/bin/topic.py?topic=25838>, <mailto:std-proposals+help@isocpp.org>
List-Archive: <http://groups.google.com/a/isocpp.org/group/std-proposals/>
List-Subscribe: <http://groups.google.com/a/isocpp.org/group/std-proposals/subscribe>,
 <mailto:std-proposals+subscribe@isocpp.org>
List-Unsubscribe: <http://groups.google.com/a/isocpp.org/group/std-proposals/subscribe>,
 <mailto:googlegroups-manage+399137483710+unsubscribe@googlegroups.com>
Xref: news.gmane.org gmane.comp.lang.c++.isocpp.proposals:11583
Archived-At: <http://permalink.gmane.org/gmane.comp.lang.c++.isocpp.proposals/11583>

------=_Part_372_32744078.1403845253991
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable



I do understand your rationale in many of the points you are raising, and I=
=20
am aware of other existing libraries, which in some cases made different=20
design decisions. However many of the choices are mutually exclusive and in=
=20
my opinion it is impossible to converge on the "single best design". Many=
=20
of the design decisions in our proposal are also compromises.

I disagree with your judgment that we should strive to find an ultimate=20
solution before moving forward with any proposal. Lack of array_view or a=
=20
similar type is clearly a gap in C++ Standard, which many libraries are=20
addressing in different ways. We took the first stab in trying to address=
=20
this deficiency with putting forward the type we are familiar with, have=20
worked with successfully for over 2 years, and in the process we were also=
=20
fixing all issues we were aware of. It surely is not rock solid -- nothing=
=20
is -- and many use-cases might have benefit from tailoring details=20
differently to cater each of them, but we have to start somewhere.
Importantly, we are not "standardizing" array_view yet. If it passes LWG=20
voting on the next Committee meeting it will be merely appended to one of=
=20
TSs (unless anything changes, this should be Library Fundamentals TS) and=
=20
on track to be considered for C++17. The three years lead time is intended=
=20
exactly to gather the wider practical experience you are concerned about.

I hope this helps to clarify our position.

On Monday, June 16, 2014 5:49:39 PM UTC-7, Jeremy Maitin-Shepard wrote:

> On Sun, Jun 15, 2014 at 9:38 PM, =C5=81ukasz Mendakiewicz <l.menda...@liv=
e.com=20
> <javascript:>> wrote:
>
>> Thank you Jeffrey for adding me on the thread and for your initial=20
>> response -- which I almost universally agree with.
>>
>> Jeremy, thank you for your detailed feedback. First, I believe that most=
=20
>> of the disagreement stems from the approach to the proposal. You've=20
>> commented that "it doesn't make sense to standardize a partial solution;=
=20
>> better to work out a good, complete interface outside the standard". Thi=
s=20
>> is opposite to the "crawl, walk, run" philosophy we have taken when writ=
ing=20
>> this document up. We recognize that we have only explored some parts of =
the=20
>> design space, because as you have rightly observed our experience is=20
>> limited to C++ AMP, which constitutes only a specific data parallel=20
>> approach to algorithms. Of course we believe that this is a solid=20
>> foundation that can be generalized and built upon, hence the proposal. B=
ut=20
>> we deliberately avoided any design decisions which may be controversial =
or=20
>> result in a prolonged tug of war, instead intending for subsequent=20
>> proposals (not necessarily authored by us) to fill the gaps. Certainly,=
=20
>> should anything being proposed now be a possible hindrance for any=20
>> reasonable extension, it must be addressed sooner than later, however I=
=20
>> think that none of the lacks pointed by you belong to this category.
>>
> I think it is basically impossible to nail down even a "minimal" interfac=
e=20
> without considering the design of a complete library.  If you looked at 1=
0=20
> different, widely-used C++ multidimensional array libraries and saw that=
=20
> all of their interfaces shared a given attribute, then standardizing that=
=20
> attribute of the interface would likely be a safe choice.  Obviously that=
=20
> is not the case here, though.
>
> Some examples of design options:
> - Make array_view parameterized by the pointer type, rather than the valu=
e=20
> type.  This allows it to hold smart pointers of various types, or a point=
er=20
> that represents GPU memory, or a smart pointer that represents GPU memory=
,=20
> etc.  If the pointer is to GPU memory, reading and writing won't work (an=
d=20
> won't be allowed at compile-time), but the indexing, slicing, sectioning=
=20
> and other operations on the array_view representation itself can still be=
=20
> used.  This is the decision I took in my own in-house library.  Allowing=
=20
> array_view to hold a smart pointer means you don't need a separate type t=
o=20
> represent an array_view with ownership.  (Sectioning and slicing produces=
 a=20
> result with the pointer decayed to a regular pointer.)  You can also use =
a=20
> __restrict__ pointer for an array_view type used as a function parameter =
to=20
> indicate lack of aliasing.
>
> - Make bounds and index just generic fixed-size vector types.  In this=20
> case iterators on bounds should iterate over the components, rather than=
=20
> iterate over coordinates within the bounds.
> =20
> - More generally, the proposal distinguishes between array_view and=20
> strided_array_view, but doesn't allow for more fine-grained information t=
o=20
> be provided at compile time, such as the number of trailing contiguous=20
> dimensions, alignment information, etc.
>
>> >>> Some particular issues with the proposed design include:=20
>> >>>=20
>> >>> - No iterators are provided for array_view or strided_array_view, a=
=20
>> clear=20
>> >>> omission in the interface.  For a multidimensional array, there are=
=20
>> two=20
>> >>> obvious types of iteration that may be desired:=20
>> >>>   1. iterating over just the first dimension;=20
>> >>>   2. iterating over a flat view of all of the elements.  For a=20
>> strided=20
>> >>> array, iterating over a flat view cannot be done efficiently without=
=20
>> >>> segmented/hierarchical iterators.=20
>> >>>   Which of these should be the default (i.e. provided through begin(=
)=20
>> and=20
>> >>> end() rather than through some adaptor) is not clear,=20
>> >>
>> >> From the Zen of Python, "In the face of ambiguity, refuse the=20
>> >> temptation to guess."=20
>> >
>> > That's true, but no adaptors to provide the functionality unambiguousl=
y=20
>> are provided either.  For the particular case of a one-dimensional array=
,=20
>> there is no ambiguity, and not being able to easily use array_view with=
=20
>> standard algorithms or range-based for is a serious usability issue.  Th=
is=20
>> will only become worse if/when standard algorithm overloads that take=20
>> ranges rather than iterator pairs are added.
>>
>> To the list of possible alternatives, I would add an option that was=20
>> proposed somewhere in the prior discussion on the proposal --=20
>> "hierarchical" array_view/elemental iterator, i.e. an iterator that for=
=20
>> array_view<T, Rank> with Rank > 1 iterates over array_view<T, Rank-1>; a=
nd=20
>> over T for Rank =3D=3D 1.
>>
> Yes, that is a better way of expressing what I meant by "iterating over=
=20
> just the first dimension".  I believe this makes the most sense, but then=
=20
> it becomes desirable to have size() be the size of the first dimension fo=
r=20
> consistency with the iterators, and instead add a num_elements() that=20
> returns the total number of elements.  The correct definition of size() i=
s=20
> hard to decide without figuring out how iterators should work.
>
> The other issue is that if the iterators are hierarchical, you need a new=
=20
> hierarchical for_each that effectively expands to N nested for loops for=
=20
> N-dimensional arrays and which can operate on multiple arrays (of the sam=
e=20
> size).  This way you can express element-wise operations without any=20
> overhead.
>
>> It is however worth pointing out that in the first round of review with=
=20
>> LEWG at Issaquah meeting, the vocal unchallenged feedback for iterators =
was=20
>> "I do not want iterators for the views", thus we did not put much effort=
=20
>> into contemplating adding any. Even though the choice for Rank =3D=3D 1 =
is=20
>> "obvious", we consider this a corner case for array_view, and would rath=
er=20
>> have a holistic approach.
>> There remains the possibility to add some form of the iterator as a=20
>> future extension.
>>
> Well, you can take my comments as a challenge to that view.  Rank =3D=3D =
1 may=20
> be a "corner" case, but is likely to be used more frequently than all oth=
er=20
> cases combined.  A 1-dimensional array view that can't be used with=20
> for-range loops, boost (or std?) range adapters, etc. is a non-starter.
>
>> >>> - No data() access member for strided_array_view.  This is presumabl=
y=20
>> >>> omitted because the memory is non-contiguous, but given that=20
>> >>> strided_array_view is a thin wrapper, there has to be a convenient=
=20
>> way to=20
>> >>> get at the underlying memory.=20
>> >>
>> >> Why "has to"?=20
>> >
>> > Because the user might want to convert the array to another=20
>> representation for use by another piece of code/library, e.g. FFTW or=20
>> Eigen, CUDA memcpy, etc.  Under the proposed interface, for a 3-dimensio=
nal=20
>> stided array view, you have to use &arr[0][0][0].  This is even worse th=
an=20
>> having to do &vec[0] for std::vector, since the same expression cannot b=
e=20
>> used independent of the dimensionality.
>>
>> data() on strided_array_view could be harmful, as there is no guarantee=
=20
>> of contiguity, which a developer / function template might assume. Pleas=
e=20
>> note that for every other case in the Standard, the range [data(),=20
>> data()+size()) is valid/safe, thus providing other semantics here would =
be=20
>> dubious.
>> Granted, one can perform &arr[0][0][0] and run afoul, but that is no=20
>> different than e.g. &my_list.front().
>> It could be possible to provide "unsafe_data()" interface, which would=
=20
>> have to be consumed along with stride(), however I do not see much benef=
it=20
>> here over just using the "proper" interface -- bounds() and operator[]()=
..
>>
> Calling it noncontiguous_data() is fine, though certainly then array_view=
=20
> should have an noncontiguous_data() (in addition to data()) as well for=
=20
> consistency.  I don't think unsafe_data is really the right name, though=
=20
> the functionality is more important than the name.  &arr[0][0][0] has the=
=20
> disadvantage that it is more verbose and doesn't allow for writing generi=
c=20
> code that works with any number of dimensions.  Just using the "proper"=
=20
> interface isn't a solution.  A fundamental component like multidimensiona=
l=20
> arrays cannot be a "walled garden".  The (pointer, bounds, strides)=20
> representation, which numerous other libraries like FFTW use as well,=20
> should be transparent.
>
>> >>> - Slicing is limited: it is not possible to take a slice in a=20
>> dimension=20
>> >>> other than the first.=20
>> >>
>> >> I think that's called "section()".=20
>> >
>> > Well, that takes a "section" but does not reduce the dimensionality=20
>> like "slice". [...] Certainly there is a large design space for general=
=20
>> subscripting and it almost certainly requires a fair amount of template=
=20
>> metaprogramming, but this is very useful functionality
>>
>> Yes, slicing and sectioning are slightly different. It is however=20
>> important to note that slicing in its current form always guarantees the=
=20
>> contiguity of the result, thus the return type may be array_view. The mo=
re=20
>> general cases (as you are describing) would result in a potentially stri=
ded=20
>> resulting view, which would need to be typed strided_array_view. This is=
=20
>> why it is important IMO to distinguish these two cases.
>> In the current form of the proposal we decided to provide the minimal=20
>> design -- only the first version, which in our opinion might be handy fo=
r=20
>> users used to C-style multidimensional addressing (i.e. arr[3][1][4] v=
=20
>> arr[{3, 1, 4}]), which actually you used above :).
>> The latter generic case can be trivially added as an extension to the=20
>> interface. I agree it may be useful, and if the current design is accept=
ed=20
>> for the TS, I encourage you to propose such extension (or nag us for fol=
low=20
>> ups :)).
>>
>> >>> - It is not clear whether it is really useful to have bounds and=20
>> index be=20
>> >>> separate types (though should probably at least be explicitly=20
>> convertible to=20
>> >>> each other).=20
>> >>
>> >> It adds a bit of type safety. Adding two bounds, for instance, doesn'=
t=20
>> >> make a lot of sense.=20
>> >
>> > I agree that type safety is in principle useful, but it is impossible=
=20
>> to predict what relationships might hold in any given program, and so th=
ere=20
>> is the question of whether the safety it adds is worth the cost of havin=
g=20
>> to explicitly work around it in the cases that it is too restrictive. =
=20
>> Certainly if they are separate types there needs to be a way to=20
>> (explicitly) convert.
>>
>> I agree with both of you :).
>> These are separate types, because their meaning and semantics are=20
>> different. It was also discussed in N3851, Appendix, II. The mental mode=
l I=20
>> use for reasoning about them is: index ~ vector and bounds ~ point (more=
=20
>> specifically: a zero-bound, axis-aligned rectangle described by its maxi=
mum=20
>> point), which makes the set of available operators apparent.
>> I understand the explicit conversions between these two types, even=20
>> though not theoretically sound, might be handy in practice and should be=
=20
>> added.
>>
> If we follow that logic, a point is a vector from the origin.  Therefore=
=20
> index should just be Vector<ptrdiff_t,N> and bounds<N> could have a=20
> Vector<ptrdiff_t,N> member.  However, it seems like it would be simpler t=
o=20
> just have bounds be Vector<ptrdiff_t,N> as well and save the trouble of t=
he=20
> explicit conversions.  What type of error does the bounds/index distincti=
on=20
> prevent?  More generally, I am not sure why we shouldn't think of an inde=
x=20
> as a point as well.  If we want the last element in an array x, we have :=
=20
> x.bounds() - 1  (or x.bounds() - {1,1, ...}).  We want to use this as an=
=20
> index, but the current operator definitions would have it be a bounds.
>
>> > As a specific example, consider if we want to compute the bounds of a=
=20
>> valid convolution if we have an array x and a filter k.  The bounds of t=
he=20
>> result are: x.bounds - k.bounds + 1.  None of the arithmetic operators=
=20
>> defined in the proposal help me with that; I'd just have to write a loop=
.. =20
>> Even if the arithmetic operators weren't limited by the bounds/index=20
>> distinction, there would still be no way to create a constant-valued=20
>> vector, i.e. to handle the constant 1.
>>
>> Explicit conversions would help with the first part, right? I hope you=
=20
>> are also not suggesting any such conversion should be implicit?
>> However I do not really understand what do you mean in the second part,=
=20
>> "a constant-valued vector". We purposefully allow to create both bounds<=
1>=20
>> and index<1> from a scalar; and disallow such operation for Rank > 1=20
>> (mostly because the semantics are not obvious, e.g. for rank 2, should=
=20
>> index<2>{16} be {0, 16}, {16, 0} or {16, 16}).
>> So net-net, in your example you should be able to do: x.bounds -=20
>> index<2>{k.bounds} + {1, 1}. It a little more long-winded, but IMO easie=
r=20
>> to comprehend than the original.
>>
> I don't think the explicit conversion to index adds to the clarity.  Also=
,=20
> the problem with {1, 1} is that it depends on the dimensionality.  The=20
> interface should be designed to be convenient for generic code.  I would=
=20
> suggest a Scalar<T> type generated by a wrapper function scalar, from whi=
ch=20
> bounds and index can be implicitly constructed and which can be used in a=
ll=20
> of the operators.
>
>> > Another related issue I didn't notice before: the only way to construc=
t=20
>> an index or bounds type is from an initializer_list.  There is no=20
>> (explicit) conversion from std::array, for instance.  It is quite likely=
=20
>> that an index or bounds value would be computed by some other code, that=
=20
>> may use a different representation.
>>
>> Yes, we should add (const value_type* begin, const value_type* end)=20
>> constructors for both types. <rant>Or it should be possible to create=20
>> initializer_list explicitly from such pair which would solve this issue=
=20
>> universally.</rant>
>>
> Okay, though initializer_list is not compile-time sized.
> =20
>
>> >>> Furthermore, bounds and index essentially represent a=20
>> >>> fixed-size discrete vector type, i.e. an augmented interface for=20
>> std::array.=20
>> >>> Arguably the interface of std::array should just be extended to add=
=20
>> >>> component-wise operators instead.  Also, a much more comprehensive=
=20
>> set of=20
>> >>> component-wise operations should be provided.  A statically-sized=20
>> vector=20
>> >>> type is sorely needed in C++, but it does not make sense to=20
>> standardize=20
>> >>> these limited, single-purposes types when they could be, and should=
=20
>> be, much=20
>> >>> more general.=20
>> >>
>> >> We don't necessarily want to go for the most general possible type al=
l=20
>> at once
>> >
>> > Certainly there is a large design space, but that is a reason to be=20
>> cautious about standardizing anything. [...]
>>
>> There are two separate issue raised here:
>> - Why novel types -- this is primarily to introduce "vocabulary" types=
=20
>> and allow to discriminate index and bounds, as they have different=20
>> semantics, as discussed before. This was also touched upon in the=20
>> previously referred N3851, Appendix, II.
>> - Why so little functionality -- I agree with Jeffrey on this, and I hav=
e=20
>> described more of the philosophy in the introduction. I would be interes=
ted=20
>> however what exactly you mean by "a much more comprehensive set of=20
>> component-wise operations".
>>
> Almost anything could be useful (and I am thinking of the perspective of =
a=20
> general fixed-size vector type, but these all apply to index/bounds as=20
> well), but some particular examples: min, max, <, <=3D, =3D=3D, !=3D, >=
=3D, >.  The=20
> relational operations need to return a vector of bool, which isn't=20
> compatible with index/bounds currently since they aren't parameterized by=
=20
> the type.  Obviously, though, it wouldn't make sense to call them index o=
r=20
> bounds if they are parameterized by a type.
> =20
>
>> Having said that, I assume this is something that can be added later as =
a=20
>> pure extension, correct?
>>
> Some of them, like min and max, could be unintrusive, though neither inde=
x=20
> nor bounds would be a particularly meaningful choice as the return type.
> =20
>
>> > Given the long turn around time for revisions to C++
>>
>> The Committee is certainly trying to get better at quicker iterations,=
=20
>> let's not be discouraged by the ghosts of the past...
>>
>> > Other omissions from index and bounds include:
>> >  1. no data() access to the components
>> >  2. no iterators over components for index or bound
>>
>> index and bounds are not containers and should not be used as such. For=
=20
>> the limited number of scenario where I want to use/modify indeterminate=
=20
>> number of components, it was always sufficient for me to use a raw loop,=
=20
>> and operator[]...
>> In case I'm wrong, these as well should be easy to add later as=20
>> extensions.
>>
>> > for_each(x_array, y_array, x_array.bounds(), [&](auto &x, auto &y, aut=
o=20
>> pos) {
>> >   if (x !=3D y) std::cout << "mismatch at position " << pos << std::en=
dl;
>> > });
>>
>> I assume the idea here is that such "for_each" uses begin(X), end(X) for=
=20
>> all provided arguments, passing dereferenced iterators to the function=
=20
>> object. If so, I do not see how the current semantics of bounds_iterator=
=20
>> would get into way here? It should just work as is, no?
>>
> The idea is that it would generate N nested for loops for N-dimensional=
=20
> array views, rather than an less efficient single for loop.
> =20
>
>> Actually, it should be possible to write the following with just the=20
>> current proposal and the current standard for_each:
>>  auto x_av =3D array_view<N>{x_array};
>>  auto y_av =3D array_view<N>{y_array};
>>  for_each(begin(x_av.bounds()), end(x_av.bounds()), [&](index<N> pos) {
>>   if(x_av[pos] !=3D y_av[pos]) std::cout << "mismatch at position " << p=
os=20
>> << std::endl;
>>  });
>>
> Yes, but note that the iteration over bounds is more expensive and the=20
> multiplication with all of the strides has to happen for every position.
> =20
>
>> > From my perspective, the only way to properly explore this design spac=
e=20
>> for multidimensional arrays/containers/ranges is through an actively=20
>> developed and widely used open source library.
>> Our (Microsoft) prototype implementation is in the pipeline to be=20
>> published as an open source project. It should happen really soon, and I=
=20
>> hope it will help to convey the discussed ideas.
>>
>
> That's good news.  I think the best library is likely to be produced if=
=20
> the users of the library and the developers of the library are one and th=
e=20
> same.  Issues become apparent and the library interface can be tweaked to=
=20
> correct them very quickly that way.
> =20

--=20

---=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.

------=_Part_372_32744078.1403845253991
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><p>I do understand your rationale in many of the points yo=
u are raising, and I am aware of other existing libraries, which in some ca=
ses made different design decisions. However many of the choices are mutual=
ly exclusive and in my opinion it is impossible to converge on the "single =
best design". Many of the design decisions in our proposal are also comprom=
ises.</p><p>I disagree with your judgment that we should strive to find an =
ultimate solution before moving forward with any proposal. Lack of array_vi=
ew or a similar type is clearly a gap in C++ Standard, which many libraries=
 are addressing in different ways. We took the first stab in trying to addr=
ess this deficiency with putting forward the type we are familiar with, hav=
e worked with successfully for over 2 years, and in the process we were als=
o fixing all issues we were aware of. It surely is not rock solid -- nothin=
g is -- and many use-cases might have benefit from tailoring details differ=
ently to cater each of them, but we have to start somewhere.</p><div>Import=
antly, we are not "standardizing" array_view yet. If it passes LWG voting o=
n the next Committee meeting it will be merely appended to one of TSs (unle=
ss anything changes, this should be Library Fundamentals TS) and on track t=
o be considered for C++17. The three years lead time is intended exactly to=
 gather the wider practical experience you are concerned about.</div><div><=
br></div><div>I hope this helps to clarify our position.</div><div><br>On M=
onday, June 16, 2014 5:49:39 PM UTC-7, Jeremy Maitin-Shepard wrote:</div><b=
lockquote class=3D"gmail_quote" style=3D"margin: 0px 0px 0px 0.8ex; padding=
-left: 1ex; border-left-color: rgb(204, 204, 204); border-left-width: 1px; =
border-left-style: solid;"><div dir=3D"ltr">On Sun, Jun 15, 2014 at 9:38 PM=
, =C5=81ukasz Mendakiewicz <span dir=3D"ltr">&lt;<a onmousedown=3D"this.hre=
f=3D'javascript:';return true;" onclick=3D"this.href=3D'javascript:';return=
 true;" href=3D"javascript:" target=3D"_blank" gdf-obfuscated-mailto=3D"ylQ=
7Z_pmbVsJ">l.menda...@live.com</a>&gt;</span> wrote:<br><div><div class=3D"=
gmail_quote">
<blockquote class=3D"gmail_quote" style=3D"margin: 0px 0px 0px 0.8ex; paddi=
ng-left: 1ex; border-left-color: rgb(204, 204, 204); border-left-width: 1px=
; border-left-style: solid;"><div dir=3D"ltr"><p>Thank you Jeffrey for addi=
ng me on the thread and for your initial response -- which I almost univers=
ally agree with.</p>
<p>Jeremy, thank you for your detailed feedback. First, I believe that most=
 of the disagreement stems from the approach to the proposal. You've commen=
ted&nbsp;that "it doesn't make sense to standardize a partial solution; bet=
ter to work out a good, complete interface outside the standard". This is o=
pposite to the "crawl, walk, run" philosophy we have taken when writing thi=
s document up. We recognize that we have only explored some parts of the de=
sign space, because as you have rightly observed our experience is limited =
to C++ AMP, which constitutes only a specific data parallel approach to alg=
orithms. Of course we believe that this is a solid foundation that can be g=
eneralized and built upon, hence the proposal. But we deliberately avoided =
any design decisions which may be controversial or result in a prolonged tu=
g of war, instead intending for subsequent proposals (not necessarily autho=
red by us) to fill the gaps. Certainly, should anything being proposed now =
be a possible hindrance for any reasonable extension, it must be addressed =
sooner than later, however I think that none of the lacks pointed by you be=
long to this category.</p>
</div></blockquote><div>I think it is basically impossible to nail down eve=
n a "minimal" interface without considering the design of a complete librar=
y.&nbsp; If you looked at 10 different, widely-used C++ multidimensional ar=
ray libraries and saw that all of their interfaces shared a given attribute=
, then standardizing that attribute of the interface would likely be a safe=
 choice.&nbsp; Obviously that is not the case here, though.<br>
<br>Some examples of design options:<br></div><div>- Make array_view parame=
terized by the pointer type, rather than the value type.&nbsp; This allows =
it to hold smart pointers of various types, or a pointer that represents GP=
U memory, or a smart pointer that represents GPU memory, etc.&nbsp; If the =
pointer is to GPU memory, reading and writing won't work (and won't be allo=
wed at compile-time), but the indexing, slicing, sectioning and other opera=
tions on the array_view representation itself can still be used.&nbsp; This=
 is the decision I took in my own in-house library.&nbsp; Allowing array_vi=
ew to hold a smart pointer means you don't need a separate type to represen=
t an array_view with ownership.&nbsp; (Sectioning and slicing produces a re=
sult with the pointer decayed to a regular pointer.)&nbsp; You can also use=
 a __restrict__ pointer for an array_view type used as a function parameter=
 to indicate lack of aliasing.<br>
<br></div><div>- Make bounds and index just generic fixed-size vector types=
..&nbsp; In this case iterators on bounds should iterate over the components=
, rather than iterate over coordinates within the bounds.<br></div><div>&nb=
sp;<br>
</div><div>- More generally, the proposal distinguishes between array_view =
and strided_array_view, but doesn't allow for more fine-grained information=
 to be provided at compile time, such as the number of trailing contiguous =
dimensions, alignment information, etc.<br>
</div><blockquote class=3D"gmail_quote" style=3D"margin: 0px 0px 0px 0.8ex;=
 padding-left: 1ex; border-left-color: rgb(204, 204, 204); border-left-widt=
h: 1px; border-left-style: solid;"><div dir=3D"ltr"><div><p>&gt;&gt;&gt; So=
me particular issues with the proposed design include: <br>&gt;&gt;&gt; <br=
>
&gt;&gt;&gt; - No iterators are provided for array_view or strided_array_vi=
ew, a clear <br>&gt;&gt;&gt; omission in the interface.&nbsp; For a multidi=
mensional array, there are two <br>&gt;&gt;&gt; obvious types of iteration =
that may be desired: <br>
&gt;&gt;&gt;&nbsp;&nbsp; 1. iterating over just the first dimension; <br>&g=
t;&gt;&gt;&nbsp;&nbsp; 2. iterating over a flat view of all of the elements=
..&nbsp; For a strided <br>&gt;&gt;&gt; array, iterating over a flat view ca=
nnot be done efficiently without <br>
&gt;&gt;&gt; segmented/hierarchical iterators. <br>&gt;&gt;&gt;&nbsp;&nbsp;=
 Which of these should be the default (i.e. provided through begin() and <b=
r>&gt;&gt;&gt; end() rather than through some adaptor) is not clear, <br>&g=
t;&gt;<br>
&gt;&gt; From the Zen of Python, "In the face of ambiguity, refuse the <br>=
&gt;&gt; temptation to guess." <br>&gt;<br>&gt; That's true, but no adaptor=
s to provide the functionality unambiguously are provided either.&nbsp; For=
 the particular case of a one-dimensional array, there is no ambiguity, and=
 not being able to easily use array_view with standard algorithms or range-=
based for is a serious usability issue.&nbsp; This will only become worse i=
f/when standard algorithm overloads that take ranges rather than iterator p=
airs are added.</p>
</div><p>To the list of possible alternatives, I would add an option that w=
as proposed somewhere in the prior discussion on the proposal -- "hierarchi=
cal" array_view/elemental iterator, i.e. an iterator that for array_view&lt=
;T, Rank&gt; with Rank &gt; 1 iterates over array_view&lt;T, Rank-1&gt;; an=
d over T for Rank =3D=3D 1.<br>
</p></div></blockquote><div>Yes, that is a better way of expressing what I =
meant by "iterating over just the first dimension".&nbsp; I believe this ma=
kes the most sense, but then it becomes desirable to have size() be the siz=
e of the first dimension for consistency with the iterators, and instead ad=
d a num_elements() that returns the total number of elements.&nbsp; The cor=
rect definition of size() is hard to decide without figuring out how iterat=
ors should work.<br>
<br></div><div>The other issue is that if the iterators are hierarchical, y=
ou need a new hierarchical for_each that effectively expands to N nested fo=
r loops for N-dimensional arrays and which can operate on multiple arrays (=
of the same size).&nbsp; This way you can express element-wise operations w=
ithout any overhead.<br>
</div><blockquote class=3D"gmail_quote" style=3D"margin: 0px 0px 0px 0.8ex;=
 padding-left: 1ex; border-left-color: rgb(204, 204, 204); border-left-widt=
h: 1px; border-left-style: solid;"><div dir=3D"ltr"><p>It is however worth =
pointing out that in the first round of review with LEWG at Issaquah meetin=
g, the vocal unchallenged feedback for iterators was "I do not want iterato=
rs for the views", thus we did not put much effort into contemplating addin=
g any. Even though the choice for Rank =3D=3D 1 is "obvious", we consider t=
his a corner case for array_view, and would rather have a holistic approach=
..<br>
There remains the possibility to add some form of the iterator as a future =
extension.</p></div></blockquote><div>Well, you can take my comments as a c=
hallenge to that view.&nbsp; Rank =3D=3D 1 may be a "corner" case, but is l=
ikely to be used more frequently than all other cases combined.&nbsp; A 1-d=
imensional array view that can't be used with for-range loops, boost (or st=
d?) range adapters, etc. is a non-starter.<br>
</div><blockquote class=3D"gmail_quote" style=3D"margin: 0px 0px 0px 0.8ex;=
 padding-left: 1ex; border-left-color: rgb(204, 204, 204); border-left-widt=
h: 1px; border-left-style: solid;"><div dir=3D"ltr"><div><p>&gt;&gt;&gt; - =
No data() access member for strided_array_view.&nbsp; This is presumably <b=
r>
&gt;&gt;&gt; omitted because the memory is non-contiguous, but given that <=
br>&gt;&gt;&gt; strided_array_view is a thin wrapper, there has to be a con=
venient way to <br>&gt;&gt;&gt; get at the underlying memory. <br>&gt;&gt;<=
br>
&gt;&gt; Why "has to"? <br>&gt;<br>&gt; Because the user might want to conv=
ert the array to another representation for use by another piece of code/li=
brary, e.g. FFTW or Eigen, CUDA memcpy, etc.&nbsp; Under the proposed inter=
face, for a 3-dimensional stided array view, you have to use &amp;arr[0][0]=
[0].&nbsp; This is even worse than having to do &amp;vec[0] for std::vector=
, since the same expression cannot be used independent of the dimensionalit=
y.</p>
</div><p>data() on strided_array_view could be harmful, as there is no guar=
antee of contiguity, which a developer / function template might assume. Pl=
ease note that for every other case in the Standard, the range [data(), dat=
a()+size()) is valid/safe, thus providing other semantics here would be dub=
ious.<br>
Granted, one can perform &amp;arr[0][0][0] and run afoul, but that is no di=
fferent than e.g. &amp;my_list.front().<br>It could be possible to provide =
"unsafe_data()" interface, which would have to be consumed along with strid=
e(), however I do not see much benefit here over just using the "proper" in=
terface -- bounds() and operator[]().</p>
</div></blockquote><div>Calling it noncontiguous_data() is fine, though cer=
tainly then array_view should have an noncontiguous_data() (in addition to =
data()) as well for consistency.&nbsp; I don't think unsafe_data is really =
the right name, though the functionality is more important than the name.&n=
bsp; &amp;arr[0][0][0] has the disadvantage that it is more verbose and doe=
sn't allow for writing generic code that works with any number of dimension=
s.&nbsp; Just using the "proper" interface isn't a solution.&nbsp; A fundam=
ental component like multidimensional arrays cannot be a "walled garden".&n=
bsp; The (pointer, bounds, strides) representation, which numerous other li=
braries like FFTW use as well, should be transparent.<br>
</div><blockquote class=3D"gmail_quote" style=3D"margin: 0px 0px 0px 0.8ex;=
 padding-left: 1ex; border-left-color: rgb(204, 204, 204); border-left-widt=
h: 1px; border-left-style: solid;"><div dir=3D"ltr"><p></p><div>&gt;&gt;&gt=
; - Slicing is limited: it is not possible to take a slice in a dimension <=
br>
&gt;&gt;&gt; other than the first. <br>&gt;&gt;<br>&gt;&gt; I think that's =
called "section()". <br>&gt;<br></div>&gt; Well, that takes a "section" but=
 does not reduce the dimensionality like "slice". [...] Certainly there is =
a large design space for general subscripting and it almost certainly requi=
res a fair amount of template metaprogramming, but this is very useful func=
tionality<p>
</p><p>Yes, slicing and sectioning are slightly different. It is however im=
portant to note that slicing in its current form always guarantees the cont=
iguity of the result, thus the return type may be array_view. The more gene=
ral cases (as you are describing) would result in a potentially strided res=
ulting view, which would need to be typed strided_array_view. This is why i=
t is important IMO to distinguish these two cases.<br>
In the current form of the proposal we decided to provide the minimal desig=
n -- only the first version, which in our opinion might be handy for users =
used to C-style multidimensional addressing (i.e. arr[3][1][4] v arr[{3, 1,=
 4}]), which actually you used above :).<br>
The latter generic case can be trivially added as an extension to the inter=
face. I agree it may be useful, and if the current design is accepted for t=
he TS, I encourage you to propose such extension (or nag us for follow ups =
:)).</p>
<div><p>&gt;&gt;&gt; - It is not clear whether it is really useful to have =
bounds and index be <br>&gt;&gt;&gt; separate types (though should probably=
 at least be explicitly convertible to <br>&gt;&gt;&gt; each other). <br>
&gt;&gt;<br>&gt;&gt; It adds a bit of type safety. Adding two bounds, for i=
nstance, doesn't <br>&gt;&gt; make a lot of sense. <br>&gt;<br>&gt; I agree=
 that type safety is in principle useful, but it is impossible to predict w=
hat relationships might hold in any given program, and so there is the ques=
tion of whether the safety it adds is worth the cost of having to explicitl=
y work around it in the cases that it is too restrictive.&nbsp; Certainly i=
f they are separate types there needs to be a way to (explicitly) convert.<=
/p>
</div><p>I agree with both of you :).<br>These are separate types, because =
their meaning and semantics are different. It was also discussed in N3851, =
Appendix, II. The mental model I use for reasoning about them is: index ~ v=
ector and bounds ~ point (more specifically: a zero-bound, axis-aligned rec=
tangle described by its maximum point), which makes the set of available op=
erators apparent.<br>
I understand the explicit conversions between these two types, even though =
not theoretically sound, might be handy in practice and should be added.</p=
></div></blockquote><div>If we follow that logic, a point is a vector from =
the origin.&nbsp; Therefore index should just be Vector&lt;ptrdiff_t,N&gt; =
and bounds&lt;N&gt; could have a Vector&lt;ptrdiff_t,N&gt; member.&nbsp; Ho=
wever, it seems like it would be simpler to just have bounds be Vector&lt;p=
trdiff_t,N&gt; as well and save the trouble of the explicit conversions.&nb=
sp; What type of error does the bounds/index distinction prevent?&nbsp; Mor=
e generally, I am not sure why we shouldn't think of an index as a point as=
 well.&nbsp; If we want the last element in an array x, we have : x.bounds(=
) - 1&nbsp; (or x.bounds() - {1,1, ...}).&nbsp; We want to use this as an i=
ndex, but the current operator definitions would have it be a bounds.<br>
</div><blockquote class=3D"gmail_quote" style=3D"margin: 0px 0px 0px 0.8ex;=
 padding-left: 1ex; border-left-color: rgb(204, 204, 204); border-left-widt=
h: 1px; border-left-style: solid;"><div dir=3D"ltr"><div><p>&gt; As a speci=
fic example, consider if we want to compute the bounds of a valid convoluti=
on if we have an array x and a filter k.&nbsp; The bounds of the result are=
: x.bounds - k.bounds + 1.&nbsp; None of the arithmetic operators defined i=
n the proposal help me with that; I'd just have to write a loop.&nbsp; Even=
 if the arithmetic operators weren't limited by the bounds/index distinctio=
n, there would still be no way to create a constant-valued vector, i.e. to =
handle the constant 1.</p>
</div><p>Explicit conversions would help with the first part, right? I hope=
 you are also not suggesting any such conversion should be implicit?<br>How=
ever I do not really understand what do you mean in the second part, "a con=
stant-valued vector". We purposefully allow to create both bounds&lt;1&gt; =
and index&lt;1&gt; from a scalar; and disallow such operation for Rank &gt;=
 1 (mostly because the semantics are not obvious, e.g. for rank 2, should i=
ndex&lt;2&gt;{16} be {0, 16}, {16, 0} or {16, 16}).<br>
So net-net, in your example you should be able to do: x.bounds - index&lt;2=
&gt;{k.bounds} + {1, 1}. It a little more long-winded, but IMO easier to co=
mprehend than the original.</p></div></blockquote><div>I don't think the ex=
plicit conversion to index adds to the clarity.&nbsp; Also, the problem wit=
h {1, 1} is that it depends on the dimensionality.&nbsp; The interface shou=
ld be designed to be convenient for generic code.&nbsp; I would suggest a S=
calar&lt;T&gt; type generated by a wrapper function scalar, from which boun=
ds and index can be implicitly constructed and which can be used in all of =
the operators.<br>
</div><blockquote class=3D"gmail_quote" style=3D"margin: 0px 0px 0px 0.8ex;=
 padding-left: 1ex; border-left-color: rgb(204, 204, 204); border-left-widt=
h: 1px; border-left-style: solid;"><div dir=3D"ltr"><div><p>&gt; Another re=
lated issue I didn't notice before: the only way to construct an index or b=
ounds type is from an initializer_list.&nbsp; There is no (explicit) conver=
sion from std::array, for instance.&nbsp; It is quite likely that an index =
or bounds value would be computed by some other code, that may use a differ=
ent representation.</p>
</div><p>Yes, we should add (const value_type* begin, const value_type* end=
) constructors for both types. &lt;rant&gt;Or it should be possible to crea=
te initializer_list explicitly from such pair which would solve this issue =
universally.&lt;/rant&gt;</p>
</div></blockquote><div>Okay, though initializer_list is not compile-time s=
ized.<br></div><div>&nbsp;</div><blockquote class=3D"gmail_quote" style=3D"=
margin: 0px 0px 0px 0.8ex; padding-left: 1ex; border-left-color: rgb(204, 2=
04, 204); border-left-width: 1px; border-left-style: solid;"><div dir=3D"lt=
r"><div>
&gt;&gt;&gt; Furthermore, bounds and index essentially represent a <br>&gt;=
&gt;&gt; fixed-size discrete vector type, i.e. an augmented interface for s=
td::array. <br>&gt;&gt;&gt; Arguably the interface of std::array should jus=
t be extended to add <br>
&gt;&gt;&gt; component-wise operators instead.&nbsp; Also, a much more comp=
rehensive set of <br>&gt;&gt;&gt; component-wise operations should be provi=
ded.&nbsp; A statically-sized vector <br>&gt;&gt;&gt; type is sorely needed=
 in C++, but it does not make sense to standardize <br>
&gt;&gt;&gt; these limited, single-purposes types when they could be, and s=
hould be, much <br>&gt;&gt;&gt; more general. <br>&gt;&gt;<br>&gt;&gt; We d=
on't necessarily want to go for the most general possible type all at once<=
br>
&gt;<br></div>&gt; Certainly there is a large design space, but that is a r=
eason to be cautious about standardizing anything. [...]<p></p><p>There are=
 two separate issue raised here:<br>- Why novel types -- this is primarily =
to introduce "vocabulary" types and allow to discriminate index and bounds,=
 as they have different semantics, as discussed before. This was also touch=
ed upon in the previously referred N3851, Appendix, II.<br>
- Why so little functionality -- I agree with Jeffrey on this, and I have d=
escribed more of the philosophy in the introduction. I would be interested =
however what exactly you mean by "a much more comprehensive set of componen=
t-wise operations".</p>
</div></blockquote><div>Almost anything could be useful (and I am thinking =
of the perspective of a general fixed-size vector type, but these all apply=
 to index/bounds as well), but some particular examples: min, max, &lt;, &l=
t;=3D, =3D=3D, !=3D, &gt;=3D, &gt;.&nbsp; The relational operations need to=
 return a vector of bool, which isn't compatible with index/bounds currentl=
y since they aren't parameterized by the type.&nbsp; Obviously, though, it =
wouldn't make sense to call them index or bounds if they are parameterized =
by a type.<br>
</div><div>&nbsp;</div><blockquote class=3D"gmail_quote" style=3D"margin: 0=
px 0px 0px 0.8ex; padding-left: 1ex; border-left-color: rgb(204, 204, 204);=
 border-left-width: 1px; border-left-style: solid;"><div dir=3D"ltr"><p>Hav=
ing said that, I assume this is something that can be added later as a pure=
 extension, correct?</p>
</div></blockquote><div></div><div>Some of them, like min and max, could be=
 unintrusive, though neither index nor bounds would be a particularly meani=
ngful choice as the return type.<br></div><div>&nbsp;</div><blockquote clas=
s=3D"gmail_quote" style=3D"margin: 0px 0px 0px 0.8ex; padding-left: 1ex; bo=
rder-left-color: rgb(204, 204, 204); border-left-width: 1px; border-left-st=
yle: solid;">
<div dir=3D"ltr"><div><p>&gt; Given the long turn around time for revisions=
 to C++</p></div><p>The Committee is certainly trying to get better at quic=
ker iterations, let's not be discouraged by the ghosts of the past...</p>
<div><p>&gt; Other omissions from index and bounds include:<br>&gt;&nbsp; 1=
.. no data() access to the components<br>&gt;&nbsp; 2. no iterators over com=
ponents for index or bound</p></div><p>index and bounds are not containers =
and should not be used as such. For the limited number of scenario where I =
want to use/modify indeterminate number of components, it was always suffic=
ient for me to use a raw loop, and operator[]...<br>
In case I'm wrong, these as well should be easy to add later as extensions.=
</p><div><p>&gt; for_each(x_array, y_array, x_array.bounds(), [&amp;](auto =
&amp;x, auto &amp;y, auto pos) {<br>&gt;&nbsp;&nbsp; if (x !=3D y) std::cou=
t &lt;&lt; "mismatch at position " &lt;&lt; pos &lt;&lt; std::endl;<br>
&gt; });</p></div><p>I assume the idea here is that such "for_each" uses be=
gin(X), end(X) for all provided arguments, passing dereferenced iterators t=
o the function object. If so, I do not see how the current semantics of bou=
nds_iterator would get into way here? It should just work as is, no?<br>
</p></div></blockquote><div>The idea is that it would generate N nested for=
 loops for N-dimensional array views, rather than an less efficient single =
for loop.<br></div><div>&nbsp;</div><blockquote class=3D"gmail_quote" style=
=3D"margin: 0px 0px 0px 0.8ex; padding-left: 1ex; border-left-color: rgb(20=
4, 204, 204); border-left-width: 1px; border-left-style: solid;">
<div dir=3D"ltr"><p>Actually, it should be possible to write the following =
with just the current proposal and the current standard for_each:<br>&nbsp;=
auto x_av =3D array_view&lt;N&gt;{x_array};<br>&nbsp;auto y_av =3D array_vi=
ew&lt;N&gt;{y_array};<br>
&nbsp;for_each(begin(x_av.bounds())<wbr>, end(x_av.bounds()), [&amp;](index=
&lt;N&gt; pos) {<br>&nbsp; if(x_av[pos] !=3D y_av[pos]) std::cout &lt;&lt; =
"mismatch at position " &lt;&lt; pos &lt;&lt; std::endl;<br>&nbsp;});</p></=
div></blockquote>
<div>Yes, but note that the iteration over bounds is more expensive and the=
 multiplication with all of the strides has to happen for every position.<b=
r></div><div>&nbsp;</div><blockquote class=3D"gmail_quote" style=3D"margin:=
 0px 0px 0px 0.8ex; padding-left: 1ex; border-left-color: rgb(204, 204, 204=
); border-left-width: 1px; border-left-style: solid;">
<div dir=3D"ltr"><p>&gt; From my perspective, the only way to properly expl=
ore this design space for multidimensional arrays/containers/ranges is thro=
ugh an actively developed and widely used open source library.</p><div>Our =
(Microsoft) prototype implementation is in the pipeline to be published as =
an open source project. It should happen really soon, and I hope it will he=
lp to convey the discussed ideas.</div>
</div></blockquote><div><br></div><div>That's good news.&nbsp; I think the =
best library is likely to be produced if the users of the library and the d=
evelopers of the library are one and the same.&nbsp; Issues become apparent=
 and the library interface can be tweaked to correct them very quickly that=
 way.<br>
</div></div></div></div>
</blockquote></div>

<p></p>

-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/">http://groups.google.com/a/isocpp.org/group/std-proposals/<=
/a>.<br />

------=_Part_372_32744078.1403845253991--

.
