2

I have lot of code like this

MyClass v = {1,2,3,4,5,6};

Unfortunately my project is targeted stupid ARM compiler that does not support fancy C++11 features, in particular it doesn't support 'initialyzer_list'. Currently the only solution looks like this:

MyClass v(6);
v[0]=1;
v[1]=2;
...

The problem it that there is lot of code like above and manual editing is disaster. Any way to overcome it with less blood? Macro, library, nice trick, anything else that can be allow find-replace-regexp with some coding in MyClass?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Noob
  • 335
  • 1
  • 8
  • If the number of input values is limited, you could define overloaded constructors for different number of values as needed, then you can instantiate the class like `MyClass v(1,2,3,4,5,6);` and wrap that in a macro to decide between `MyClass v(1,2,3,4,5,6)`, `Myclass v{1,2,3,4,5,6}`, and `MyClass v = {1,2,3,4,5,6}` based on compiler needs. – Remy Lebeau Jun 14 '16 at 16:14
  • 1
    C++ containers usually accept another range as argument for initialization. That are two iterators, one pointing to the beginning, one to the end. Initialize an `static const int[]` with braced initialization, then pass it boundaries to your ctor. Ultimatively 2 instead of 1 lines of code. – Sebastian Hoffmann Jun 14 '16 at 16:14
  • Also see http://stackoverflow.com/questions/2236197/what-is-the-easiest-way-to-initialize-a-stdvector-with-hardcoded-elements – Sebastian Hoffmann Jun 14 '16 at 16:16
  • This is what [Boost.Assign](http://www.boost.org/libs/assign/) is for. – ildjarn Jun 15 '16 at 06:51

3 Answers3

3

If you can stand to declare the list as an array a line earlier, you can get the size as a template argument:

class MyClass {
  public:
    template <std::size_t N>
    MyClass(const int (&in)[N]);
};

int main() {
    static const int arr[] = {1,2,3,4,5,6};
    MyClass mc(arr);
}
Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
  • One more step with macro _INIT_MYCLASS(v, {1,2,3,4,5}) and can be pretty much find-replace. Thanks!!! – Noob Jun 14 '16 at 18:36
  • @Noname to be pedantic, any symbol starting with an underscore followed by an uppecase letter is reserved for the implementation, but yes that macro would expand correctly afaict. The only issue is choosing a variable name that doesn't collide with anything else but should be doable. – Ryan Haining Jun 14 '16 at 20:15
  • I implemented it like this: `#define _MY_CLASS(v, ...) static const uint8 arr_##v[] = __VA_ARGS__; MyClass v(arr_##v)` – Noob Jun 14 '16 at 21:09
  • 1
    @Noname : Again, `_MY_CLASS` is illegal for user code. – ildjarn Jun 15 '16 at 06:52
2
template<class T>
struct span {
  T* b;
  T* e;
  span( T* s, T* f ):b(s), e(f) {}

  T* begin() const { return b; }
  T* end() const { return e; }
  std::size_t size() const { return end()-begin(); }
};

template<class T, std::size_t N>
struct chain_array {
  T arr[N];

  T const& operator[](std::size_t i) const {return arr[i];}
  std::size_t size() const { return N; }
  T const* begin() const { return arr; }
  T const* end() const { return arr+N; }

  operator span<T>() const {
    return span<T>(begin(), end());
  }

  chain_array<T, N+1> operator,( T rhs ) {
    chain_array<T, N+1> r;
    for (std::size_t i=0; i < N; ++i)
      r[i]=(*this)[i];
    r[N] = rhs;
    return r;
  }
};

template<class T>
chain_array<T, 1> chain( T t ) {
  chain_array<T, 1> r;
  r[0] = t;
  return r;
}

now write this:

MyClass v = (chain(1),2,3,4,5,6);

and add an overload of MyClass::MyClass(span<T>).

This solution is inefficient because I create the intermediate arrays, and they are not discarded until the end of the line. A smart compiler could fix this.

We could create a chain of pointers or references (basically, an expression template) which we only collapse when we cast to the final il<T> type if we cared.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

As a slight alternative to Ryan Haining's solution above, you don't actually need to put the array in a variable the line before:

#include <iostream>

struct A {
  template<size_t N>
  A(const int (&list)[N]) {
    size = N;
    myArr = new int[N];
    int *tmp = myArr;
    for(const int *i = list; i < list + N; ++i) {
      *tmp++ = *i;
    }
  }

  ~A() {
    delete[] myArr;
  }

  int *myArr;
  size_t size;
};

int main(int argc, char **argv) {
  A a = (int[]) {1, 2, 3, 4, 5};
  for(int i = 0; i < a.size; ++i) {
    std::cout << a.myArr[i] << std::endl;
  }
  return 0;
}

Because of copy elision and implicit constructors, you can get away with just a cast on the same line. The array is implicitly converted to your type, and then the temp value is moved to the stack dynamic storage efficiently through copy elision. This means revising your code would just require a couple of (type[]) and reworking your constructors to be array based instead of initializer_list based. Unfortunately, without c++11 foreach loops this can be a little annoying, but with some pointer works it's not awful, and you can always make some convenience macros like:

#define FOREACH(type, name, arr, N)\
  for(type *__foreach__ptr__ = arr, (name) = *__foreach__ptr__;\
  __foreach__ptr__ < (arr) + (N);\
  ++__foreach__ptr__, (name) = *__foreach__ptr__)

which would be used like:

int N = 5;
int arr[N] = {1, 2, 3, 4, 5};
FOREACH(int, val, arr, N) {
  std::cout << val << std::endl;
}