如何更好地处理依赖于模板参数的类成员类型?

时间:2012-04-03 23:52:54

标签: c++ templates opencl specialization

我正在尝试编写一些代码,将原生C ++类型的数组转换为OpenCL标准定义的适当大小的矢量类型。

Endian-ness和打包是OpenCL实现特定的。 OpenCL类型不提供方便的运算符[]。 (实际上API是C)另一个问题:cl_int4有一个.s3成员,但cl_int2没有。

我有一些功能正常的东西,但你可以看到我已经走进模板疯狂的土地。

这可以用更好的方式完成吗?这些函数不会经常调用,因此更好将是减少的程序二进制文件大小和更少冗长的源代码的组合

这是我到目前为止所得到的。我没有向你展示所有的尺寸特化(省略3-6),我也想实现至少整数类型。

#include <CL/cl.h>

template < typename HOST_T, int NUM_DIM >
struct Payload_t;

// Vector length needs to be (for dims 1-6):  2, 4, 8, 8, 16, 16

//single precision

template < >
struct __attribute__((packed)) Payload_t <float, 1> { 
    cl_float2 vec;
    void setElement( int pos, float value )
    {
        switch (pos) {
            case 0: vec.s0 = value; return;
            case 1: vec.s1 = value; return;
            default: return;
        }
    }
};

template < >
struct __attribute__((packed)) Payload_t <float, 2> { 
    cl_float4 vec;
    void setElement( int pos, float value )
    {
        switch (pos) {
            case 0: vec.s0 = value; return;
            case 1: vec.s1 = value; return;
            case 2: vec.s2 = value; return;
            case 3: vec.s3 = value; return;
            default: return;
        }
    }
};

/// double-precision

template < >
struct __attribute__((packed)) Payload_t <double, 1> { 
    cl_double2 vec;
    void setElement( int pos, double value )
    {
        switch (pos) {
            case 0: vec.s0 = value; return;
            case 1: vec.s1 = value; return;
            default: return;
        }
    }
};

template < >
struct __attribute__((packed)) Payload_t <double, 2> { 
    cl_double4 vec;
    void setElement( int pos, double value )
    {
        switch (pos) {
            case 0: vec.s0 = value; return;
            case 1: vec.s1 = value; return;
            case 2: vec.s2 = value; return;
            case 3: vec.s3 = value; return;
            default: return;
        }
    }
};

我想你可能会好奇我将如何使用这个课程。在一个例子中,我有一个类型为REAL的类,它有一个以下成员类的实例,其中有一个Payload_t的实例。

template <int NUM_DIM >
struct cartesian_box_descriptor_t : cartesian_box_descriptor_base_t
{
    static const int vectorLengthArray[6];
    void set_dx( REAL * dx_vec )
    {
        for (int i = 0; i < NUM_DIM; ++i)
            payload.setElement( i, dx_vec[i] );
    };
    void set_startx( REAL * startx_vec )
    {
        for (int i = 0; i < NUM_DIM; ++i)
            payload.setElement( NUM_DIM + i , startx_vec[i] );
    };

    virtual WxAny getDescriptorStruct() const
    {
        return WxAny( payload ); // packages this simple structure as 'scalar' with hidden type
    };


    Payload_t< REAL, NUM_DIM> payload;
};

getDescriptorStruct()打包OpenCL支持的类型,我可以将其作为内核参数发送到OpenCL API,所有字节都落在正确的位置。

如果有人在考虑范式转换,我只需要立即设置整个矢量。

1 个答案:

答案 0 :(得分:0)

我不确定是为此感到骄傲还是感到羞耻,但它确实有效。您确实需要确保对set()的所有调用都使用完全正确的类型。它自动处理新的cl_类型和cl_类型的新大小,仅在3个位置发生变化。如果有人感到如此倾向,它可能会被进一步清理。

#include<iostream>
#include<assert.h>

struct cl_float1 {
  float s0;
};

struct cl_float2 {
  float s0;
  float s1;
};


#define ZERO_THROUGH_15(pre) \
  pre i0;            \
  pre i1;            \
  pre i2;            \
  pre i3;            \
  pre i4;            \
  pre i5;            \
  pre i6;            \
  pre i7;            \
  pre i8;            \
  pre i9;            \
  pre i10;           \
  pre i11;           \
  pre i12;           \
  pre i13;           \
  pre i14;           \
  pre i15


template<typename SIMD, typename POD>
struct offset {
  static POD SIMD::* data[16];
  ZERO_THROUGH_15(static bool);
  offset() {
    ZERO_THROUGH_15();
  }
};
template<typename SIMD, typename POD>
/*static*/ POD SIMD::* offset<SIMD,POD>::data[16];

template<int n>
struct offsetGetter {
  template<typename SIMD, typename POD>
  static POD SIMD::* get(...) {
    return NULL;
  }
};

#define GET_OFFSET(n) \
template<> \
struct offsetGetter<n> { \
  template<typename SIMD, typename POD, POD SIMD::* OFS> \
  struct check {}; \
\
  template<typename SIMD, typename POD> \
  static POD SIMD::* get(check<SIMD, POD, &SIMD::s ## n>*) { \
    return &SIMD::s ## n; \
  } \
\
  template<typename SIMD, typename POD> \
  static POD SIMD::* get(...) { \
    return NULL; \
  } \
  template<typename SIMD, typename POD> \
  static bool init() { \
    offset<SIMD,POD>::data[n] = get<SIMD,POD>(NULL);    \
  }; \
}; \
template<typename SIMD, typename POD> \
 /*static*/ bool offset<SIMD,POD>::i##n = offsetGetter<n>::init<SIMD,POD>()

GET_OFFSET(0);
GET_OFFSET(1);
GET_OFFSET(2);
GET_OFFSET(3);
GET_OFFSET(4);
GET_OFFSET(5);
GET_OFFSET(6);
GET_OFFSET(7);
GET_OFFSET(8);
GET_OFFSET(9);
GET_OFFSET(10);
GET_OFFSET(11);
GET_OFFSET(12);
GET_OFFSET(13);
GET_OFFSET(14);
GET_OFFSET(15);

template<typename SIMD, typename POD>
void set(SIMD& simd, int n, POD val) {
  offset<SIMD,POD> ignoreme;
  POD SIMD::* ofs = offset<SIMD,POD>::data[n];
  assert(ofs);
  simd.*ofs = val;
}

main(){
  cl_float2 x;
  set(x, 0, 42.0f);
  std::cout << x.s0 << std::endl; // prints 42
  set(x, 1, 52.0f);
  std::cout << x.s1 << std::endl; // prints 52
  cl_float1 y;
  set(y, 1, 42.0f); // assertion failure
}