1

I want to use a templated structure in order to mimic an array of doubles in 4 dimensions, the maximum size for each dimension is know in compilation time. Therefore, I think that using templates structure will give a chance to gain performance. You can find my attempt for the implementation bellow. The code compiles unless that I attempt to instantiate one structure. I don't understand what is the problem with the code bellow, suggestions will be very appreciated.

Even more, I want to do two improvements if possible: 1) I want to be capable of use data of type float and of type double 2) Will be fancy to have some kind of overloaded operator that enables to assign values to the records of data in a similar way as T(N,L,M,J)=val in place of having to use T.assign(N,L, M,J,value). Again, suggestions will be very appreciated.

My aim is to fill the data in T_4D as fast as possible.

#include <iostream>
#include <cstring> // for memset                                                                                                                                                                                                             
using namespace std;

template <size_t dim_N=3,size_t dim_L=3,size_t dim_M=3,size_t dim_J=10,double *data=NULL>
struct T_4D {

  enum {SIZE = dim_N*dim_L*dim_M*dim_J };
  enum {LEN1 = dim_N };
  enum {LEN2 = dim_L };
  enum {LEN3 = dim_M };
  enum {LEN4 = dim_J };



  static void create()
  {
    data=(double *)malloc(SIZE*sizeof(double));
    memset(data, 0.0, SIZE*sizeof(*data));
  }

  static size_t multi_index(const size_t N) {
    return N;
  }
  static size_t multi_index(const size_t N,const size_t L) {
    return L*dim_N + N;
  }
  static size_t multi_index(const size_t N,const size_t L, const size_t M) {
    return (M*dim_L + L)*dim_N + N;
  }
  static size_t multi_index(const size_t N,const size_t L, const size_t M,const size_t J) {
    return ((J*dim_M + M)*dim_L + L)*dim_N + N;
  }

  double operator()(size_t N,size_t L, size_t M, size_t J){
    return data[multi_index(N,L,M,J)];
  }

  static void assign(size_t N,size_t L, size_t M, size_t J,double value){
    data[multi_index(N,L,M,J)]=value;
    }


};

int main()
{
  double *instance;
  T_4D<3,3,3,10,instance> T;
  T.create();

  return 0;
}

The compilation errors are:

./main.cpp: In function ‘int main()’:
./main.cpp:49:17: error: the value of ‘instance’ is not usable in a constant expression
   T_4D<3,3,3,10,instance> T;
                 ^
./main.cpp:48:11: note: ‘instance’ was not declared ‘constexpr’
   double *instance;
           ^
./main.cpp:49:25: error: ‘instance’ is not a valid template argument because ‘instance’ is a variable, not the address of a variable
   T_4D<3,3,3,10,instance> T;
                         ^
./main.cpp:50:5: error: request for member ‘create’ in ‘T’, which is of non-class type ‘int’
   T.create();
     ^
Makefile:197: recipe for target 'obj/main.o' failed
make: *** [obj/main.o] Error 1

2 Answers2

1

Using double* data = NULL as a template parameter does not seem right. You can use a double* as a template parameter but you can't assign to it as you are doing with:

data=(double *)malloc(SIZE*sizeof(double));

You can remove that as a template parameter and make it a member variable of the class.

template <size_t dim_N=3,size_t dim_L=3,size_t dim_M=3,size_t dim_J=10>
struct T_4D {

  double* data;

  ...

and then allocate memory for it in the constructor instead of in the static member function.

T_4D() : data(new double[SIZE])
{
   memset(data, 0.0, SIZE*sizeof(*data));
}

Remember to follow The Rule of Three and The Rule of Five since you are allocating memory from the heap.

Then, main can simply be:

int main()
{
  T_4D<3,3,3,10> T;
  return 0;
}
Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
1

If all of your dimensions are known at compile time, there is no need for you to allocate dynamic memory. Simply use:

std::aligned_storage_t<sizeof( T ) * SIZE, alignof( T )> data;

You don't even need to initialize anything since you're working with POD types. If you want to zero the memory out, just use this:

for ( std::size_t i = 0; i < SIZE; ++i )
    *( reinterpret_cast<T*>( &data ) + i ) = 0;

This will be the most efficient implementation, since we use static contiguous memory. You'll have to implement proper indexing, but that's not too difficult.

Actually, just use T data[ SIZE ]; or std::array<T, SIZE> data.

Also, remove the double* template parameter, these cannot be changed, so it can't be used for your data.

user2296177
  • 2,807
  • 1
  • 15
  • 26
  • Thanks for help, to implement the initialization I added a reste function but it is producing the error "./main.cpp:19:39: error: invalid cast from type ‘std::aligned_storage<2160ul, 8ul>’ to type ‘double*’ *( reinterpret_cast( data ) + i ) = 0;" – Rubén Darío Guerrero Jul 08 '16 at 23:36
  • @RubénDaríoGuerrero it was missing a `&`. Fixed. My bad, I'm answering from my phone. Remember to use the `_t` version; if it's not available, use: `typename std::aligned_storage::type`. – user2296177 Jul 08 '16 at 23:44
  • Ok, problem solved. Another detail: when I attempt to assign values to data I get the error:" ./main.cpp:119:14: error: request for member ‘assign’ in ‘T.T_4D::data’, which is of non-class type ‘std::aligned_storage<8ul, 8ul>::type [270]’ T.data.assign(N,L, M, J,0.1);" any idea? – Rubén Darío Guerrero Jul 09 '16 at 01:18
  • @RubénDaríoGuerrero you need to reinterpret_cast. I suggest you simply ignore aligned storage and replace it with `T data[ SIZE ]` or `std::array`. It'll make your life easier. – user2296177 Jul 09 '16 at 04:29
  • Thanks for the suggestion, but I like the initial way. the assignment can be done as *( reinterpret_cast( &data ) + multi_index(N,L,M,J) ) = T(0.1); – Rubén Darío Guerrero Jul 09 '16 at 05:32