MSVC对于对齐的结构无法使用stl vector的问题之解决

九月 21st, 2008 by Soloman | Print MSVC对于对齐的结构无法使用stl vector的问题之解决 | 948 views

在CUDA编程中,结构的对齐(alignment)是非常重要的。在我写的CUDA 结构对齐的C++模拟这篇文章中,我介绍了如何在C++里模拟CUDA对结构的内存对齐机制,从而使得我们可以在C++里构造结构,然后传到CUDA里进行处理。但是随后我又遇到一个问题。比如,我们有这么一个对齐了的结构:

template <unsigned int Len, typename ValueType>
struct ALIGN16 Foo
{
    ValueType x[Len];
    ...
};

当我们程序里使用了std::vector< Foo<5, float> > v(1);,也就是构造一个内存自管理的数组,并设置其初始大小为1。这段代码在g++里能够正常编译,但当我们用msvc(我用的是vc80)来编译的话,则会遇到报错:

cl /nologo /TP /EHsc /wd4819 /DBOOST_BIND_ENABLE_STDCALL /DBOOST_MEM_FN_ENABLE_S
TDCALL /O2 /MD /I. /c test\base\test_carray.cpp /Fotest\base\test_carray.obj
test_carray.cpp
C:\Program Files\Microsoft Visual Studio 8\VC\INCLUDE\vector(694) : error C2719:
 '_Val': formal parameter with __declspec(align('16')) won't be aligned
        test\base\test_carray.cpp(278) : see reference to class template instant
iation 'std::vector<_Ty>' being compiled
        with
        [
            _Ty=carray_type
        ]
scons: *** [test\base\test_carray.obj] Error 2
scons: building terminated because of errors.

我们搜索STL vector的694行,发现有这样的代码:

	void resize(size_type _Newsize, _Ty _Val)
		{	// determine new length, padding with _Val elements as needed
		if (size() < _Newsize)
			_Insert_n(end(), _Newsize - size(), _Val);
		else if (_Newsize < size())
			erase(begin() + _Newsize, end());
		}

问题就出在其第二个参数上,我们可以看到,这是个传值的参数,需要在函数调用的参数栈上创建一个临时的参数,但当这个类型是被对齐了的话,msvc编译器就会报错。

那么,怎么解决呢?直接修改STL代码,显然不是一个好方法。这时我想到了模板的特例化,可以用这种非侵入式的改变来解决这个问题,同时保证不修改STL代码。

首先,我们创建一个新的文件,比如叫foo_vector.hpp,并大致写成:

#ifndef FOO_VECTOR_HPP
#define FOO_VECTOR_HPP

// only msvc need to be fix
#ifdef _MSC_VER // 1400 for vc80

#include <vector>
#include "foo.hpp"

namespace std
{

// our specialization goes here...

}

#endif // _MSC_VER

#endif

这里,我们首先判断编译器,如果是msvc,才进行我们的特例化,因为g++没有这个问题(并且我发现,msvc使用的STL代码和g++使用的STL代码是不相同的)。这里我没有进行msvc版本的判断,因为是demo嘛。

然后打开msvc自带的STL vector代码(在”Microsoft Visual Studio 8\VC\include\vector”),将整个vector类的定义拷贝过来,大致在421行到1246行之间。然后修改模板的头,改为我们的特例化:

// TEMPLATE CLASS vector
template<unsigned int Len, typename ValueType,
    class _Ax>
    class vector < Foo<Len, ValueType>, _Ax >
    : public _Vector_val<Foo<Len, ValueType>, _Ax>
{	// varying size array of values
public:
    typedef Foo<Len, ValueType> _Ty;
    typedef vector<_Ty, _Ax> _Myt;
    typedef _Vector_val<_Ty, _Ax> _Mybase;
    .......

这样,我们就完成了vector对我们Foo结构的特例化,接下来找到刚才产生问题的resize()方法,改为:

    void resize(size_type _Newsize, const _Ty & _Val)
    { // determine new length, padding with _Val elements as needed
        if (size() < _Newsize)
            _Insert_n(end(), _Newsize - size(), _Val);
        else if (_Newsize < size())
            erase(begin() + _Newsize, end());
    }

注意其中红色部分,将传值改为传参,避免在参数栈上创建被对齐的结构的对象。然后,在我们使用std::vector< Foo<5, float> >之前包含我们的foo_vector.hpp头文件,就可以正常使用了。

, , , ,

 

添加评论 (支持Gravatar头像)

注意: 评论者允许使用'@user空格'的方式将自己的评论通知另外评论者。例如, ABC是本文的评论者之一,则使用'@ABC '(不包括单引号)将会自动将您的评论发送给ABC。使用'@all ',将会将评论发送给之前所有其它评论者。请务必注意user必须和评论者名相匹配(大小写一致)。

实时评论预览