opencl:C++11下使用别名(x,y,z,hi,lo...)访问vector类型(cl_int2,cl_long16...)的元素

在gcc(5.2.0)下使用C++11写opencl的主机端代码时,发现无法像内核代码一样对cl_int2这样的向量(vector)类型用pos.x,pos.y这样的别名来访问向量元素,只能用pos.s[0]这种数组访问的方式。这是为什么?
这本是个小问题,但本人是个完美主义者,总想搞个清楚,最后总算搞清楚了,于是就有了本文。

这是platform.hcl_int2的定义,可以看出,虽然代码中有,x,y名字定义,但编译开关__CL_HAS_ANON_STRUCT__导致这部分代码是灰的/无效的
这里写图片描述


opencl内核代码中向量元素的访问

在opencl内核代码中,对于opencl中的向量类型,既可以使用s0~sF(根据向量长度不同)来访问向量中的指定元素,也可以用元素的别名来访问(x,y,z,w,hi,lo…)
比如向量数据float4 ,是由4个float组成的向量

float4 f;
float s0=f.s0; //f中第一个元素
float s0=f.x;  //与前一行等价
float2 f2=f.hi //f中前2个元素组成的float2

可以看出,使用x,y,hi,lo这样的别名,代码更加直观易懂。
opencl主机端向量类型的定义

这些向量类型在主机端都有等价的向量类型定义,区别就是类型名字加了cl_前缀,如内核代码中int2类型在主机端是cl_int2,内核代码中float4类型在主机端是cl_float4
参见下面的cl_float4的定义:

typedef union
{
    cl_float  CL_ALIGNED(16) s[4];
#if __CL_HAS_ANON_STRUCT__
   __CL_ANON_STRUCT__ struct{ cl_float   x, y, z, w; };
   __CL_ANON_STRUCT__ struct{ cl_float   s0, s1, s2, s3; };
   __CL_ANON_STRUCT__ struct{ cl_float2  lo, hi; };
#endif
#if defined( __CL_FLOAT2__) 
    __cl_float2     v2[2];
#endif
#if defined( __CL_FLOAT4__) 
    __cl_float4     v4;
#endif
}cl_float4;
// 摘自cl_platform.h

从上面cl_float4的定义可以看出主机端的cl_float4是个联合体,默认是以数字下标访问向量元素的(s[0],s[1],s[2],s[3])。同时它也支持以别名(x,y,z,w,s0~s3)访问元素。

编译器差异

不过你也看到了这些别名都定义在匿名结构体(anonymous struct)中,而匿名结构体并不是C语言标准的一部分,是编译器自行实现的,所以__CL_HAS_ANON_STRUCT__宏开关决定编译器是否支持匿名结构体(anonymous struct),控制着是否允许使用别名访问元素。
于是我顺藤摸瓜找到__CL_HAS_ANON_STRUCT__定义的位置,就是下面这段代码(中文部分是作者加的注释)

/* Define capabilities for anonymous struct members. */
#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ )
// gcc下如果没定义__STRICT_ANSI__,则__CL_HAS_ANON_STRUCT__为1
#define  __CL_HAS_ANON_STRUCT__ 1
#define  __CL_ANON_STRUCT__ __extension__
#elif defined( _WIN32) && (_MSC_VER >= 1500)
// VS2008以后支持匿名结构体,但会有警告,所以这里会有关闭C4201警告
   /* Microsoft Developer Studio 2008 supports anonymous structs, but
    * complains by default. */
#define  __CL_HAS_ANON_STRUCT__ 1
#define  __CL_ANON_STRUCT__
   /* Disable warning C4201: nonstandard extension used : nameless
    * struct/union */
#pragma warning( push )
#pragma warning( disable : 4201 )
#else
// gcc下如果定义了__STRICT_ANSI__,则__CL_HAS_ANON_STRUCT__为0
#define  __CL_HAS_ANON_STRUCT__ 0
#define  __CL_ANON_STRUCT__
#endif
// 摘自cl_platform.h

上面这段代码控制了__CL_HAS_ANON_STRUCT__ 的定义,可以看出,在使用gcc编译时,__CL_HAS_ANON_STRUCT__是否为1,取决于是否定义了__STRICT_ANSI__
如果定义了__STRICT_ANSI__ __CL_HAS_ANON_STRUCT__为0,否则为1。
也就是说,在gcc下编译,如果定义__STRICT_ANSI__ 就没办法使用别名访问向量元素。
下图就是我在Eclipse+MinGW(5.2.0)环境下打开cl_platform.h看到的__CL_HAS_ANON_STRUCT__ 的定义,说明__STRICT_ANSI__ 被定义了,
这里写图片描述

根本原因

那么接下来的问题就是:__STRICT_ANSI__ 是个什么鬼?
关于__STRICT_ANSI__ 来历,请参见我的上一篇博客[《C++11:MinGW当指定-std=c++11选项时 默认定义了__STRICT_ANSI__》]1

从这篇博客的标题就可以得知,如果编译代码时使用了-ansi选项,编译器就会定义__STRICT_ANSI__ ,我找遍了整个项目代码,确信没有使用过-ansi(太高端我从来不知道这个选项),所以并不是因为我使用了-ansi才造成这个问题,而是因为我使用了-std=c++11选项导致编译器自动定义了__STRICT_ANSI__

解决方案

知道了问题的根本原因,解决问题的办法也就有了。
方案1:
第一个办法就是前述博客中最后提到的办法:在使用-std=c++11选项的同时,加上-U__STRICT_ANSI__选项, 用于去掉__STRICT_ANSI__ 定义
如果你是用cmake来编译项目代码,可以在CMakeList.txt中加入这样的代码

#判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持,并去掉__STRICT_ANSI__定义
if(CMAKE_COMPILER_IS_GNUCXX)
	add_compile_options(-std=c++11)
	message(STATUS "optional:-std=c++11")	
	add_compile_options(-U__STRICT_ANSI__)
	message(STATUS "optional:-U__STRICT_ANSI__")	
endif(CMAKE_COMPILER_IS_GNUCXX)

方案二
修改你的源代码,在#include <CL/cl.hpp>#include <CL/opencl.h>语句之前使用#undef __STRICT_ANSI__删除__STRICT_ANSI__定义

#if defined( __GNUC__) && defined( __STRICT_ANSI__ )
#define __STRICT_ANSI__DEFINED__
//删除__STRICT_ANSI__定义
#undef __STRICT_ANSI__	
#endif
#include <CL/cl.hpp>
#ifdef __STRICT_ANSI__DEFINED__
#undef __STRICT_ANSI__DEFINED__
//恢复__STRICT_ANSI__定义
#define __STRICT_ANSI__
#endif

代码做上述修改后,重新rebuild index,
这里写图片描述

再打开cl_platform.h看到的__CL_HAS_ANON_STRUCT__ 的定义,说明__STRICT_ANSI__ 没有被定义,
这里写图片描述
这时再看cl_int的定义,也正常了
这里写图片描述

这两种解决方案,你可以根据自己的需要来选择,但第二种方案的没有副作用,不会影响项目中其他部分代码的编译。第一种方案会有潜在的副作用,就是可能会影响项目中与opencl无关的代码的编译。
[1]:http://blog.csdn.net/10km/article/details/51105863

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值