views:

78

answers:

2

Bear with me because I'm self taught in C++ and am spending my limited extra time on the job to try to learn more about it (I'm a chemical engineering researcher by day).

I have a pretty simple objective: 1. Make a size-safe container to store a long list of floats. 2. Make a specialized version of that container that acts as a matrix.

What I've come up with thus far, based on some feedback on various questions I've posed here is:

template<typename T>
class VectorDeque
{
public:

  void resize_index(unsigned int index) {
    if ( my_container == VECTOR ) {
      try {
        my_vector.resize(index);
        my_container = VECTOR;
      }
      catch(std::bad_alloc &e) {
        my_deque.resize(index);
        my_container = DEQUE;
      }
    }
    else if ( my_container == DEQUE ) {
      my_deque.resize(index);
    }
  }

  T operator[](unsigned int index) { 
    T ret_val;
    if ( STORAGE_CONTAINER == VECTOR ) {
      ret_val = my_vector[index];
    }
    else if ( STORAGE_CONTAINER == DEQUE ) {
      ret_val = my_deque[index];
      }
  }
private:
  enum STORAGE_CONTAINER { NONE, DEQUE, VECTOR };

  std::vector<T> my_vector;
  std::deque<T> my_deque;
  STORAGE_CONTAINER my_container;

  T& get(int index) { 
    T temp_val;
    if(my_container == VECTOR) {
      temp_val = my_vector[index];
    }
    else if(my_container == DEQUE) {
      temp_val = my_deque[index];
    }

    return temp_val;
  }

};

template<typename T>
class VectorDeque2D: public VectorDeque<T>
{
public:

  template<typename T>
  class VectorDeque2D_Inner_Set
  {
    VectorDeque2D& parent;
    int   first_index;
  public:
    // Just init the temp object
    VectorDeque2D_Inner_Set(My2D& p, int first_Index) : 
      parent(p), 
      first_Index(first_index) {} 
    // Here we get the value.
    T& operator[](int second_index)  const 
    { return parent.get(first_index,second_index);}   
  };

  // Return an object that defines its own operator[] that will access the data.
  // The temp object is very trivial and just allows access to the data via 
  // operator[]
  VectorDeque2D_Inner_Set<T> operator[](unsigned int first_index) { 
    return  VectorDeque2D_Inner_Set<T>(*this, first_index);
  }

  void resize_index_second(unsigned int second_index) {
    if ( my_container == VECTOR ) {
      try {
        for (unsigned int couter=0;couter < my_vector.size(); counter++) {
          my_vector[counter].resize(second_index);
        }
        my_container = VECTOR;
      }
      catch(std::bad_alloc &e) {
        for (unsigned int couter=0;couter < my_deque.size(); counter++) {
          my_deque[counter].resize(second_index);
        }
        my_container = DEQUE;
      }
    }
    else if ( my_container == DEQUE ) {
      for (unsigned int couter=0;couter < my_deque.size(); counter++) {
        my_deque[counter].resize(second_index);
      }
    }
  }

  void resize(unsigned int first_index,
          unsigned int second_index) {
    if ( my_container == VECTOR ) {
      try {
        my_vector.resize(first_index);
        for (unsigned int couter=0;couter < my_vector.size(); counter++) {
          my_vector[counter].resize(second_index);
        }
        my_container = VECTOR;
      }
      catch(std::bad_alloc &e) {
        my_deque.resize(first_index);
        for (unsigned int couter=0;couter < my_deque.size(); counter++) {
          my_deque[counter].resize(second_index);
        }
        my_container = DEQUE;
      }    
    }
    else if ( my_container == DEQUE ) {
      my_deque.resize(first_index);
      for (unsigned int couter=0;couter < my_deque.size(); counter++) {
        my_deque[counter].resize(second_index);
      }
    }
  }
private:
  enum STORAGE_CONTAINER { NONE, DEQUE, VECTOR };

  friend class VectorDeque2D_Inner_Set;

  std::vector<std::vector<T> > my_vector;
  std::deque<std::deque<T> > my_deque;
  STORAGE_CONTAINER my_container;

  T& get(int first_index,int second_index) { 
    T temp_val;
    if(my_container == VECTOR) {
      temp_val = my_vector[first_index][second_index];
    }
    else if(my_container == DEQUE) {
      temp_val = my_deque[first_index][second_index];
    }

    return temp_val;
  }

};

With this implementation I tried to:
1. Present the user of the wrapper with two options for access (".get(x,y)" and "[x][y]")
2. Maximize reuse by having a based wrapped class and then inheriting it to make the matrix.
3. Solve the problem of transitioning from a vector to a deque if the continuous memory limit is hit.

Does this seem like a decent solution? Suggestions?

A: 

Transitioning between the two during a single usage seems unlikely to be worth the effort, to me.

If you want to do this via roll your own, you could define a second template parameter that allows the container type to be specified at compile-time. Then you would not need both vector and deque as members and the type switching code goes away.

template<typename T, typename CONTAINER>
class VectorDeque
{
// snip
private:
  CONTAINER<T> _storage;
};
Steve Townsend
But then I don't have the automatic determination of container type based on if a badalloc is detected... I could do that with your method, but I'd need another layer of wrapping. I'd prefer to avoid a double wrapper.
Jason R. Mick
@Jason - yes I understand. Just don't think that your bad_alloc handling will be very useful in the real world. Just use `deque` all the time if you are worried about fragmentation. I guess it depends how big you expect the structs to get. btw do you have a good reason for doing this yourself?
Steve Townsend
Well... because I'm creating a tool that parses a file containing a very long list of particle coordinates. I've seen examples of this file that are gigabytes in length. Of course reading is very slow process for longer files, so the speed up of the vector would be appreciated if it didn't cause memory issues. Basically I want to optimize the solution for speed/reliability in a case where you are reading a VERY large file, typically -- a good candidate for optimization imo.
Jason R. Mick
I see. If there is anyway to size the file and preallocate enough space in your container, that would be optimal.
Steve Townsend
The header to the file contains the number of entries stored within, so it will just be a one time resize. I just wanted to wrap it (instead of an unwrapped try/catch) for ease of access, e.g. so I don't have to keep writing if statements to determine which to access the vector or deque.
Jason R. Mick
@Jason - are the elements contiguous in your file? You may be able to memory map it and use vector by providing a custom allocator constrained to just alloc for an initial vector::reserve by mapping the file and storing a base offset. Then you would have to convert file mapping offsets to and from what's required for vector to work. This way you offload the issue of memory availability to the OS, while using the simplest container in your class.
Steve Townsend
+2  A: 

Have you looked at Boost::Matrix? There's a lot of numeric and linear algebra things already built within that library.

EDIT:

After reading your comment about transitioning from a vector to a deque when size limits are reached, go with deque. Getting "fancy" like that is slowing your productivity down. Focus on the problem at hand and let the collection worry about the memory. Deque is quite fast for large arrays and only suffers when you release memory in comparison to vector.

wheaties
It may be slowing my productivity down, but I think its worthwhile as I'm trying to create a reusable tool that can be used to store VERY large coordinate matrices -- possibly a gigabyte long. Some will be shorter, though, so in the case of say 10 to 100 MB, the speed up of the vector would be greatly appreciated...
Jason R. Mick
@Jason: If you're talking on the orders of MBs, deque is probably going to be better in pretty much every case.
Billy ONeal
@Jason There's almost no difference in performance between a deque and a vector. Both given constant time look-up. See here: http://www.codeproject.com/KB/stl/vector_vs_deque.aspx
wheaties
hrmm. so theres no performance advantage to a vector? People were insisting to me there was.... I am looping through the list at the end and printing the values... I noticed that benchmark didn't include a looping access speed test, which someone told me was slow with deques.
Jason R. Mick