www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - I need some help here.

reply Damian <damian.pop gmail.com> writes:
Suppose I have a class like this:

class cl{
    private:
        char[][] _data;
        int _maxDataLength;
    public:
        char[][] data(){
            _return data;
        }
        void data(char[][] d){
            _maxDataLength = calculateMaxDataLength(d);
            _data = d;
        }
        int maxDataLength(){
            return _maxDataLength;
        }
    private:
        int calculateMaxDataLength(char[][] data){
            // this takes time.
        }
}

And in a function I do something like this:
// dClass is a global cl object.

    char[][] d = dClass.data;
    d[3] = "naranja";
    int dlength = dClass.maxDataLength;

then dlength won't be valid, since _data has changed but
_maxDataLength wasn't updated.

How can I avoid this?
I know that 

        int maxDataLength(){
            return calculateMaxDataLength(_data);            
        }

will solve the problem, but I don't want to call 
calculateMaxDataLength if the intention is to to see 
the data without modifying it.

Thanks.
Aug 30 2006
parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Damian wrote:
 Suppose I have a class like this:
 
 class cl{
     private:
         char[][] _data;
         int _maxDataLength;
     public:
         char[][] data(){
             _return data;
         }
         void data(char[][] d){
             _maxDataLength = calculateMaxDataLength(d);
             _data = d;
         }
         int maxDataLength(){
             return _maxDataLength;
         }
     private:
         int calculateMaxDataLength(char[][] data){
             // this takes time.
         }
 }
 
 And in a function I do something like this:
 // dClass is a global cl object.
 
     char[][] d = dClass.data;
     d[3] = "naranja";
     int dlength = dClass.maxDataLength;
 
 then dlength won't be valid, since _data has changed but
 _maxDataLength wasn't updated.
 
 How can I avoid this?
 I know that 
 
         int maxDataLength(){
             return calculateMaxDataLength(_data);            
         }
 
 will solve the problem, but I don't want to call 
 calculateMaxDataLength if the intention is to to see 
 the data without modifying it.
 
 Thanks.

Two ways. First way, if it makes sense you might overload the index operators for your class. If that doesn't make logical sense, then there is the .dup property which will create a safe copy: # char[][] data () { # return _data.dup; # } I don't remember ever using .dup with multi-dimensional arrays, though, so it may have to be something more like: # char[][] data () { # char[][] copy; # copy.length = _data.length; # foreach (i, x; _data) { # copy[i] = x.dup; # } # return copy; # } I would say experiment with the first one first. I imagine it should work fine, and is certainly less work than the second. -- Chris Nicholson-Sauls
Aug 30 2006
parent reply Reiner Pope <reiner.pope REMOVE.THIS.gmail.com> writes:
Chris Nicholson-Sauls wrote:
 Damian wrote:
 Suppose I have a class like this:

 class cl{
     private:
         char[][] _data;
         int _maxDataLength;
     public:
         char[][] data(){
             _return data;
         }
         void data(char[][] d){
             _maxDataLength = calculateMaxDataLength(d);
             _data = d;
         }
         int maxDataLength(){
             return _maxDataLength;
         }
     private:
         int calculateMaxDataLength(char[][] data){
             // this takes time.
         }
 }

 And in a function I do something like this:
 // dClass is a global cl object.

     char[][] d = dClass.data;
     d[3] = "naranja";
     int dlength = dClass.maxDataLength;

 then dlength won't be valid, since _data has changed but
 _maxDataLength wasn't updated.

 How can I avoid this?
 I know that
         int maxDataLength(){
             return calculateMaxDataLength(_data);                    }

 will solve the problem, but I don't want to call 
 calculateMaxDataLength if the intention is to to see the data without 
 modifying it.

 Thanks.

Two ways. First way, if it makes sense you might overload the index operators for your class. If that doesn't make logical sense, then there is the .dup property which will create a safe copy: # char[][] data () { # return _data.dup; # } I don't remember ever using .dup with multi-dimensional arrays, though, so it may have to be something more like: # char[][] data () { # char[][] copy; # copy.length = _data.length; # foreach (i, x; _data) { # copy[i] = x.dup; # } # return copy; # } I would say experiment with the first one first. I imagine it should work fine, and is certainly less work than the second. -- Chris Nicholson-Sauls

This is really the immutability problem again. Basically, you want to either: - not allow modification of _data; or - be aware when _data is changed. Chris's suggestion (dup'ing it) achieves the first, and providing the index operator achieves the second by intercepting indexing calls. Another suggestion, if you don't want to overload indexing for your class is to create an array wrapper which notifies your class on changes: class MyArray(T) { private T[] _data; T opIndexAssign(uint index, T value) { notifyThatDataHasChanged(); return _data[index] = value; } T opIndex(uint index) { return _data[index]; } ... } This means that you can use your class's opIndex for something else. Alternatively, you could use this interface to return an immutable version of _data, just by not supporting opIndexAssign. Cheers, Reiner
Aug 31 2006
next sibling parent Damian <damian.pop gmail.com> writes:
 
 Two ways.  First way, if it makes sense you might overload the index 
 operators for your class.  If that doesn't make logical sense, then 
 there is the .dup property which will create a safe copy:
 
 # char[][] data () {
 #   return _data.dup;
 # }
 
 I don't remember ever using .dup with multi-dimensional arrays, though, 
 so it may have to be something more like:
 
 # char[][] data () {
 #   char[][] copy;
 #   copy.length = _data.length;
 #   foreach (i, x; _data) {
 #     copy[i] = x.dup;
 #   }
 #   return copy;
 # }
 
 I would say experiment with the first one first.  I imagine it should 
 work fine, and is certainly less work than the second.
 
 -- Chris Nicholson-Sauls

This is really the immutability problem again. Basically, you want to either: - not allow modification of _data; or - be aware when _data is changed. Chris's suggestion (dup'ing it) achieves the first, and providing the index operator achieves the second by intercepting indexing calls. Another suggestion, if you don't want to overload indexing for your class is to create an array wrapper which notifies your class on changes: class MyArray(T) { private T[] _data; T opIndexAssign(uint index, T value) { notifyThatDataHasChanged(); return _data[index] = value; } T opIndex(uint index) { return _data[index]; } ... } This means that you can use your class's opIndex for something else. Alternatively, you could use this interface to return an immutable version of _data, just by not supporting opIndexAssign. Cheers, Reiner

Well, I thought I was missing a "const" somewhere, but it's a little more complicated. :S 1st choice: Make a copy of 2-dim array seems to be a waste of time to this specific problem. 2nd choice: My class is not a "data" class, it just works with some data, so overload index ops in my class isn't clear, I think. 3rd choice:I don't want to make a class wrapper since all I need is an array, but well, I think I can do it once and use it once and again, so I'll take this choice! Anyway, I think I miss "const". Why is not part of D? Thanks you both for your help!
Aug 31 2006
prev sibling parent reply Damian <damian.pop gmail.com> writes:
 
 class MyArray(T) {
 	private T[] _data;
 	
 	T opIndexAssign(uint index, T value) {
 		notifyThatDataHasChanged();
 		return _data[index] = value;
 	}
 
 	T opIndex(uint index) {
 		return _data[index];
 	}
 	...
 }
 
 This means that you can use your class's opIndex for something else. 
 Alternatively, you could use this interface to return an immutable 
 version of _data, just by not supporting opIndexAssign.
 

I would have to use MyArray!(MyArray!(char)) _data; for _data to be really immutable. Anyway, I can't do this that way, since my class "cl" couldnt modify _data either. I think I will do something like this: private: char[][] _data; int _maxDataLength; public: /** _DO NOT_ modify data this way, use "void data(char[][] d)" */ char[][] data(){ _return data; } void data(char[][] d){ _maxDataLength = calculateMaxDataLength(d); _data = d; }
Aug 31 2006
next sibling parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Damian wrote:
class MyArray(T) {
	private T[] _data;
	
	T opIndexAssign(uint index, T value) {
		notifyThatDataHasChanged();
		return _data[index] = value;
	}

	T opIndex(uint index) {
		return _data[index];
	}
	...
}

This means that you can use your class's opIndex for something else. 
Alternatively, you could use this interface to return an immutable 
version of _data, just by not supporting opIndexAssign.

I would have to use MyArray!(MyArray!(char)) _data; for _data to be really immutable. Anyway, I can't do this that way, since my class "cl" couldnt modify _data either.

Actually yes it could, as all members of the same module have implicit "friend" relationships. So, since 'c1' and the wrapper class (which should probably be a nested class anyhow) would be in the same module, 'c1' would have access to the wrapper's private 'data' field. -- Chris Nicholson-Sauls
Aug 31 2006
prev sibling parent Reiner Pope <reiner.pope REMOVE.THIS.gmail.com> writes:
Damian wrote:
 I would have to use 
 MyArray!(MyArray!(char)) _data;
 for _data to be really immutable.
 Anyway, I can't do this that way, since my class "cl" couldnt modify
 _data either.

No, that's not quite what I meant. Your class (cl) would look like this: class cl{ private: char[][] _data; int _maxDataLength; public: MyArray!(MyArray!(char)) data() { // Construct a MyArray which represents a readonly view of the data } int maxDataLength(){ return _maxDataLength; } private: int calculateMaxDataLength(char[][] data){ // this takes time. } } The constructor would be annoying to write, though, because you would need to recursively generate one for each level of the array. However, a bit of template magic could be your solution here, I think. Yes, from your other post, const is really what you need, and many people find it lacking. However, it is way too complex to rush, and there has not yet been a perfect solution proposed. There probably never will be, so D may remain without const. Cheers, Reiner
Aug 31 2006