1

I have something like:

Array definition:

array<array<Value, N_FOO_PER_BAR>, N_BAR> arr;

Access function:

Value getFoo(int barIdx, int fooIdx){
  return arr[barIdx][fooIdx];
}

For-loop:

for(int i = 0; i < N_BAR; i ++){
  for(int j = 0; j < N_FOO_PER_BAR; j ++){
    arr[i][j].doSomething();
  }
} 

Problem: Indices for foo and bar can easily get mixed up when using getFoo(...).

I would like to define a type BarIdx and FooIdx and then the compiler should complain when I mix them up.

The access function would then look like:

function getFoo(BadIdx barIdx, FooIdx fooIdx){
  return arr[barIdx][fooIdx];
}

The for-loop could look like:

for(BarIdx i = 0; i < N_BAR; i ++){
  for(FooIdx j = 0; j < N_FOO_PER_BAR; j ++){
    arr[i][j].doSomething();
  }
} 

So I'm looking how to define a type which (a) will issue a warning/error when used at the wrong place but (b) still behaves as much as an integer as possible, for for loop and array access with []. This should also work on CUDA, so I would prefer to use simple language constructs rather than a perfect solution.

Michael
  • 7,407
  • 8
  • 41
  • 84
  • I personally think it's a non-issue in this case, but maybe [boost::units](http://www.boost.org/doc/libs/1_55_0/doc/html/boost_units.html) can help you out. – Ami Tavory Jun 04 '15 at 10:27
  • Shoudn't `function getFoo(...)` be written `Value getFoo(...)` ? kinda obvious but maybe not for everybody. Interesting question anyhow. – kebs Jun 04 '15 at 10:35
  • @kebs, that's what happens when you use too many programming languages at the same time. It's fixed now. – Michael Jun 04 '15 at 10:41

2 Answers2

2

The difficulty with doing this is that you can't simply inherit from int in C++, so you need to provide a wrapper which not only implicitly converts to and from int, but also provides all the necessary operators so that you can do things like:

BarIdx a = 0;
++a;

You might want to start with something simple like this:

template <typename T>
class IntegralWrapper {
    T value;
public:
    typedef T value_type;
    IntegralWrapper() :value() {}
    IntegralWrapper(T v) :value(v) {}
    operator T() const { return value; }
};

Then add any operators you need later. You would then define your index classes like this:

class BarIdx : public IntegralWrapper<int> {};
class FooIdx : public IntegralWrapper<int> {};

Alternatively, here is a seemingly pretty complete integral wrapper solution taken from this question which you could probably use.

Community
  • 1
  • 1
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Can I expect that the compiler will implement this as efficient as a plain int? – Michael Jun 04 '15 at 10:41
  • 1
    You might get a small efficiency hit, but all the operators should get inlined, so I doubt it would be too bad. Only way to find out is to try it and measure. – TartanLlama Jun 04 '15 at 10:43
2

It will not trivial. The problems is not in defining the type for index, but that the operator [] of the array take size_type (std::size_t) which will reduce to null all your effort to differenciate the indexes.

I will suggest another kind of sintactic sugar:

struct Idx {int bar, foo;};

Access function:

Value& getValue(Idx i){
  return arr[i.bar][i.foo];
}

For-loop:

for(Idx i{0,0} ; i.bar < N_BAR; i.bar ++){
  for(i.foo = 0; i.foo < N_FOO_PER_BAR; i.foo ++){
    getValue(i).doSomething();
  }
} 

but also:

for(auto &foos : arr){
  for(auto &foo : foos){
    foo.doSomething();
  }
} 
qPCR4vir
  • 3,521
  • 1
  • 22
  • 32