2

I'm trying to implement a hypercubeclass, that is, multidimensional vectors. I have a problem generalizing it. I'm able to make one for a three dimensional hypercube, but as mentioned, the problem is generalizing it. Could anyone help me? You should be able to write hypercube<4> w(5) to get 4 dimensions and 5 elements in each vector that is 5*5*5*5 elements in total.

Here is the code I have for the three dimensional version:

#include <vector>
using std::vector;

using namespace std;

template <int b> 
class Hypercube {
public:

Hypercube(int a) : intvec(a){
    for (int i = 0; i<a;i++) {
        intvec[i].resize(a);
        for (int j = 0;j<a;j++) {
            intvec[i][j].resize(a);
        }
    }
}
vector<vector<int> >& operator[](int i) {
    return intvec[i];
 }

vector<vector<vector<int> > > intvec;
};
Xeo
  • 129,499
  • 52
  • 291
  • 397
shizzle
  • 183
  • 2
  • 12

2 Answers2

2

For this to work, you need recursive inheritence to provide the correct vector type and the initialization function. Both work recursively, for which I created a little helper struct called hcube_info:

// hypercube.h
#include <vector>

template<unsigned N>
struct hcube_info;

template<>
struct hcube_info<1>
{ // base version
  typedef std::vector<int> type;
  static type init(unsigned innerdim, int value = 0){
      return type(innerdim, value);
  }
};

template<unsigned N>
struct hcube_info
{ // recursive definition, N dimensions
private:
  typedef hcube_info<N-1> base;
  typedef typename base::type btype;

public:
  typedef std::vector<btype> type;
  static type init(unsigned innerdim, int value = 0){
      return type(innerdim, base::init(innerdim, value));
  }
};

As you can see, recursion all the way to the one dimensional base case. We also need to recursively initialize the vector to pass the inner dimension all the way through.

And now the real class, a nice interface around the hcube_info:

template<unsigned N>
struct hypercube
{
private:
  typedef hcube_info<N> info;
  typedef typename info::type vec_type;

public:
  typedef typename vec_type::value_type value_type;
  typedef typename vec_type::size_type size_type;

  explicit hypercube(unsigned innerdim, unsigned value = 0)
    : c(info::init(innerdim, value))
  {
  }

  value_type& operator[](unsigned i){
    return c[i];
  }

  size_type size() const{ return c.size(); }

private:
  vec_type c;
};

Test program:

#include "hypercube.h"
#include <iostream>

int main(){
  hypercube<4> c(5);
  unsigned s = c.size() * // dim 1
               c[0].size() * // dim 2
               c[0][0].size() * // dim 3
               c[0][0][0].size(); // dim 4
  std::cout << s << '\n'; // outputs: 625 -> 5 * 5 * 5 * 5 -> 5^4
}
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • What do you need the inheritance in `hcube_info` for? – bitmask Dec 20 '11 at 22:16
  • @bitmask: To get the correct vector type and the correct init function? – Xeo Dec 20 '11 at 22:19
  • 1
    You're `typedef`'ing `base` anyway (and it doesn't have any `protected` members), so I don't see what inheriting from it should be good for, but it also doesn't hurt. – bitmask Dec 20 '11 at 22:23
  • @bitmask: D'oh, now I see what you mean. Yeah, it doesn't make sense, you're absolutely right. – Xeo Dec 20 '11 at 22:25
0

I would suggest something along those lines:

template <typename T, unsigned dim> class HQ {
  std::vector<HQ<T,(dim-1)> > vector;
  public:
    HQ(unsigned size) : vector(size,HQ<T,(dim-1)>(size)) {}
};

template <typename T> class HQ<T,1> {
  std::vector<T> vector;
  public:
    HQ(unsigned size) : vector(size,T()) {}
};

template <typename T> class HQ<T,0> {};

You can then implement your accessors for the first both templates as you wish. You can also make things a bit more simple and robust by allowing zero-dimensional matrices:

template <typename T, unsigned dim> class HQ {
  std::vector<HQ<T,(dim-1)> > vector;
  public:
    HQ(unsigned size) : vector(size,HQ<T,(dim-1)>(size)) {}
};

template <typename T> class HQ<T,0> {
  T data;
  public:
    HQ(unsigned size) : data() {}
};

I imagine an access operator would look something like this:

template <typename T, unsigned dim> HQ<T,(dim-1)>& HQ<T,dim>::operator[](unsigned i) {
  return vector[i];
}
template <typename T, unsigned dim> HQ<T,(dim-1)> const& HQ<T,dim>::operator[](unsigned i) const {
  return vector[i];
}

such that you can write

HQ<int,4> hq(5);
hq[1][4][2][0] = 77;
bitmask
  • 32,434
  • 14
  • 99
  • 159
  • I get compiler error when i do this: template class Hypercube { public: Hypercube (int a) : dim(a) { Vector > dims(a); } Hypercube () : dim(2) { if (b>1) { Vector > dims(2); //something is wrong with this recursive part?! } } Vector& operator[](int i) { return dim; } Vector dim; } – shizzle Dec 20 '11 at 22:44
  • What's a `Vector` in your example, and what do you want to accomplish with `dims`, because I'm pretty sure that's not what you want. – bitmask Dec 20 '11 at 22:53
  • Vector is a vectorclass i have implemented myself, but i can use the standard one if that is the problem. – shizzle Dec 20 '11 at 23:01
  • @shizzle: I edited the `operator[]` code, maybe it's that. Otherwise I suggest you provide more information than "I keep getting errors" or post a completely new question. – bitmask Dec 20 '11 at 23:09
  • I get this error : no match for ‘operator[]’ in ‘a[0]’ when i try to do this: Hypercube a(2); a[0][0] = 5; – shizzle Dec 20 '11 at 23:21
  • @shizzle: All I can tell you is that it works on the code I proposed. If you change that, I'm afraid, I cannot help you. The snippet you dumped in an earlier comment has too many mistakes to discuss them in a comment. – bitmask Dec 20 '11 at 23:27
  • do you think you can help me over msn or something, cause i really want to learn this? – shizzle Dec 20 '11 at 23:32