digitalmars.D.learn - Getting a range over a const Container
- Matthias Walter (7/7) Jun 15 2012 Hi,
- Francisco Soulignac (79/79) Jul 18 2012 Hi all,
- Jonathan M Davis (5/7) Jul 18 2012 Right now? I'm pretty sure that that's impossible. Hopefully that will c...
- Artur Skawina (41/134) Jul 19 2012 import std.algorithm, std.range;
- Ellery Newcomer (2/4) Jul 19 2012 anyone mind cluing me in on why this is possible?
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (29/33) Jul 19 2012 It is the same as in C++. Considering the hidden non-const this and
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (28/38) Jul 19 2012 Ha ha! The output is worthless when both functions print the same thing....
- Ellery Newcomer (2/6) Jul 19 2012 cool beans, thanks.
- Ellery Newcomer (3/7) Jul 20 2012 it looks like you could almost merge these two into one using inout, but...
- Jonathan M Davis (16/25) Jul 20 2012 If everywhere that you use const, you can use inout, then it may work. B...
- Matthias Walter (7/14) Jul 18 2012 Well it doesn't work yet. But in principle it could since we can always
Hi, I have a const std.container object (e.g., a const(Array!int)) of which I'd like to have a range which can traverse that container having read-only access. This does not seem to be possible with opSlice(). Is there an alternative? Best regards, Matthias
Jun 15 2012
Hi all, it's been a while since this question, and I don't know how to solve it either. The following code passes all the test using the last version of dmd (2.059). import std.container, std.algorithm; //non const case void assertequal(T)(SList!(T) l, int[] r) { assert(equal(l[], r)); } //const case void const_assertequal(T)(const SList!(T) l, int[] r) { assert(equal(l[], r)); } unittest{ SList!(int) l; l.insertFront(2); l.insertFront(1); assertequal (l, [1,2]); assert(!__traits(compiles, const_assertequal(l, [1,2]))); } The conflict with the last assertion is that opSplice can't be applied to const. So, I looked at the container module, and made a minimal example of a list myself (emulating SList). struct List { struct Node { Node* next; int val; } private Node* first; struct Range { //sorry about "actual", it should have been current private Node* actual; this(Node* first) {actual = first;} property front() {return actual.val;} property empty() const {return !actual;} void popFront() {assert(!empty); actual = actual.next;} Range save() {return this;} } unittest{static assert(isForwardRange!(Range));} void add(int v) { Node * next = first; first = new Node; first.val = v; first.next = next; } Range opSlice() { return Range(first); } } void assertequal(L : List)(L l, int[] r) { assert(equal(l[], r)); } void assertequal_const(L : List)(L l, int[] r) { assert(equal(l[], r)); } unittest{ List l; l.add(2); l.add(1); assert(equal(l[], [1,2])); assertequal (l, [1,2]); assert(!__traits(compiles, const_assertequal(l, [1,2]))); } As far as I know, the problem comes with the transitivity of const. In assertequal_const, l.first has type const(Node*), thus it can't be converted to Node* in Range's constructor. I can't manage to find a workaround here, because l.first will always have type const(Node*), but for traversing the list I require to copy l.first into some node, say actual, whose type is Node* so that I can move it doing actual = actual.next. I would be happy to do actual = cast(Node*)(l.first) actual = actual.next; but that code is suppose to be undefined, isn't it? (http://dlang.org/const3.html : Removing Immutable With A Cast). So, my question is how can I (correctly) traverse a const SList, const DList, etc? Best, Francisco. PS: sorry for the long mail.
Jul 18 2012
On Thursday, July 19, 2012 04:39:26 Francisco Soulignac wrote:So, my question is how can I (correctly) traverse a const SList, const DList, etc?Right now? I'm pretty sure that that's impossible. Hopefully that will change, but getting const and ranges to work together can be rather difficult, and std.container needs more work in that regard. - Jonathan M Davis
Jul 18 2012
On 07/19/12 06:39, Francisco Soulignac wrote:it's been a while since this question, and I don't know how to solve it either. The following code passes all the test using the last version of dmd (2.059). import std.container, std.algorithm; //non const case void assertequal(T)(SList!(T) l, int[] r) { assert(equal(l[], r)); } //const case void const_assertequal(T)(const SList!(T) l, int[] r) { assert(equal(l[], r)); } unittest{ SList!(int) l; l.insertFront(2); l.insertFront(1); assertequal (l, [1,2]); assert(!__traits(compiles, const_assertequal(l, [1,2]))); } The conflict with the last assertion is that opSplice can't be applied to const. So, I looked at the container module, and made a minimal example of a list myself (emulating SList). struct List { struct Node { Node* next; int val; } private Node* first; struct Range { //sorry about "actual", it should have been current private Node* actual; this(Node* first) {actual = first;} property front() {return actual.val;} property empty() const {return !actual;} void popFront() {assert(!empty); actual = actual.next;} Range save() {return this;} } unittest{static assert(isForwardRange!(Range));} void add(int v) { Node * next = first; first = new Node; first.val = v; first.next = next; } Range opSlice() { return Range(first); } } void assertequal(L : List)(L l, int[] r) { assert(equal(l[], r)); } void assertequal_const(L : List)(L l, int[] r) { assert(equal(l[], r)); } unittest{ List l; l.add(2); l.add(1); assert(equal(l[], [1,2])); assertequal (l, [1,2]); assert(!__traits(compiles, const_assertequal(l, [1,2]))); } As far as I know, the problem comes with the transitivity of const. In assertequal_const, l.first has type const(Node*), thus it can't be converted to Node* in Range's constructor. I can't manage to find a workaround here, because l.first will always have type const(Node*), but for traversing the list I require to copy l.first into some node, say actual, whose type is Node* so that I can move it doing actual = actual.next. I would be happy to do actual = cast(Node*)(l.first) actual = actual.next; but that code is suppose to be undefined, isn't it? (http://dlang.org/const3.html : Removing Immutable With A Cast). So, my question is how can I (correctly) traverse a const SList, const DList, etc?import std.algorithm, std.range; struct List { static struct Node { Node* next; int val; } private Node* first; static struct Range(E) { private E* current; this(FE:const E)(FE* first) {current = first;} property front() {return current.val;} property empty() const {return !current;} void popFront() {assert(!empty); current = current.next;} property Range save() {return this;} } static assert(isForwardRange!(Range!Node)); void add(int v) { Node * next = first; first = new Node; first.val = v; first.next = next; } Range!Node opSlice() { return Range!Node(first); } Range!(const Node) opSlice() const { return Range!(const Node)(first); } } void assertequal(L : List)(L l, int[] r) { assert(equal(l[], r)); } void assertequal_const(L : List)(const L l, int[] r) { assert(equal(l[], r)); } void main() { List l; l.add(2); l.add(1); assert(equal(l[], [1,2])); assertequal (l, [1,2]); assertequal_const(l, [1,2]); } artur
Jul 19 2012
On 07/19/2012 02:51 AM, Artur Skawina wrote:Range!Node opSlice() { return Range!Node(first); } Range!(const Node) opSlice() const { return Range!(const Node)(first); }anyone mind cluing me in on why this is possible?
Jul 19 2012
On 07/19/2012 06:09 PM, Ellery Newcomer wrote:On 07/19/2012 02:51 AM, Artur Skawina wrote:It is the same as in C++. Considering the hidden non-const this and const this parameters, one of the member functions is a better match for each call: import std.stdio; struct S { void foo(string name) { writeln("Called on ", name); assert(typeid(this) == typeid(S)); } void foo(string name) const { writeln("Called on ", name); assert(typeid(this) == typeid(const S)); } } void main() { auto a = S(); const b = S(); a.foo("a"); // matches non-const foo() b.foo("b"); // matches const foo() } The output: Called on a Called on b AliRange!Node opSlice() { return Range!Node(first); } Range!(const Node) opSlice() const { return Range!(const Node)(first); }anyone mind cluing me in on why this is possible?
Jul 19 2012
On 07/19/2012 06:16 PM, Ali Çehreli wrote:On 07/19/2012 06:09 PM, Ellery Newcomer wrote:Ha ha! The output is worthless when both functions print the same thing. :) This is better: import std.stdio; struct S { void foo(string name) { writeln("non-const foo called on ", name); assert(typeid(this) == typeid(S)); } void foo(string name) const { writeln("const foo called on ", name); assert(typeid(this) == typeid(const S)); } } void main() { auto a = S(); const b = S(); a.foo("a"); // matches non-const foo() b.foo("b"); // matches const foo() } Now the output is different: non-const foo called on a const foo called on b AliOn 07/19/2012 02:51 AM, Artur Skawina wrote:It is the same as in C++. Considering the hidden non-const this and const this parameters, one of the member functions is a better match for each call:Range!Node opSlice() { return Range!Node(first); } Range!(const Node) opSlice() const { return Range!(const Node)(first); }anyone mind cluing me in on why this is possible?
Jul 19 2012
On 07/19/2012 06:18 PM, Ali Çehreli wrote:Now the output is different: non-const foo called on a const foo called on b Alicool beans, thanks.
Jul 19 2012
On 07/19/2012 06:09 PM, Ellery Newcomer wrote:On 07/19/2012 02:51 AM, Artur Skawina wrote:it looks like you could almost merge these two into one using inout, but I'm not sure what you do about selecting the return type.Range!Node opSlice() { return Range!Node(first); } Range!(const Node) opSlice() const { return Range!(const Node)(first); }
Jul 20 2012
On Friday, July 20, 2012 17:15:53 Ellery Newcomer wrote:On 07/19/2012 06:09 PM, Ellery Newcomer wrote:If everywhere that you use const, you can use inout, then it may work. But the return type is not only a difference in constness. It's a completely different type. The other problem is that the const opSlice is actually _very_ broken as far as range-based functions typically go. Whil opSlice doesn't technically require it (though it probably should), it's _very_ common for function using opSlice to assign the result to the original range, and that's not possible with this code. I keep meaning to bring it up for discussion in the newsgroup, but it's a big problem. You basically need tail-const, and I don't know of any way to implement it, because the only way to get implicit conversions is to use alias this, and that results in a recursive template instantiation, which kills the compiler. I don't think that it's actually possible to properly implement a const opSlice for many ranges right now. - Jonathan M DavisOn 07/19/2012 02:51 AM, Artur Skawina wrote:it looks like you could almost merge these two into one using inout, but I'm not sure what you do about selecting the return type.Range!Node opSlice() { return Range!Node(first); } Range!(const Node) opSlice() const { return Range!(const Node)(first); }
Jul 20 2012
On 07/19/2012 06:44 AM, Jonathan M Davis wrote:On Thursday, July 19, 2012 04:39:26 Francisco Soulignac wrote:Well it doesn't work yet. But in principle it could since we can always copy a const pointer to a non-const one to const data: Node* actual; // but 'this' is const and hence the type is const(Node*) const(Node)* i_will_traverse = actual; Best regards, MatthiasSo, my question is how can I (correctly) traverse a const SList, const DList, etc?Right now? I'm pretty sure that that's impossible. Hopefully that will change, but getting const and ranges to work together can be rather difficult, and std.container needs more work in that regard.
Jul 18 2012