运行环境VC2005.
工程里面写了这么一段代码,目的是为了实现DWORD<==>DWORD*之间完成转型而不报warning
Obj_T union_cast(Src_T src)
... {
// 编译期检查Src_T和Obj_T的数据大小是一致。
//
BOOST_MPL_ASSERT_MSG(sizeof Obj_T == sizeof Src_T, obj_type_size_is_not_same_with_src_type, (Obj_T, Src_T)); //因为利用了union的特性,所以要保证转型的变量大小要相等
union
...{
Src_T m_src;
Obj_T m_obj;
} tmp_union;
tmp_union.m_src = src;
return tmp_union.m_obj;
}
刚开始只在一个.cpp文件里面使用没有什么问题,但是在两个或以上.cpp(比如1.cpp和2.cpp)里面使用就出现问题了,会报如下联结错误:
看着比较恐怖,但是实际上就是说union_cast里面assert_arg函数被重复定义了,奇怪了,我又没有定义过assert_arg这个函数,那肯定是BOOST_MPL_ASSERT_MSG这个宏干的事情了(这个宏实际上完成的工作是,编译期的assert),查看下这个宏的定义,是这么写的:
struct msg;
typedef struct BOOST_PP_CAT(msg,__LINE__) : boost::mpl::assert_
... {
static boost::mpl::failed ************ (msg::************ assert_arg()) types_
...{ return 0; }
} BOOST_PP_CAT(mpl_assert_arg,__LINE__);
enum ... {
BOOST_PP_CAT(mpl_assertion_in_line_,__LINE__) = sizeof(
boost::mpl::assertion_failed<(c)>( BOOST_PP_CAT(mpl_assert_arg,__LINE__)::assert_arg() )
)
}
哈,凶手出来了,宏展开之后会定义一个函数
... { return 0; }
不要被这个函数吓倒,我把它分开来写,或许你就知道了:
static _ret assert_arg() ... { return 0; }
虽然有那么多*,但是不过就是个函数指针,*不过是为了出错信息更容易看出来,毕竟模板出错信息返回一大堆,return 0就是个NULL指针,其实后面继续研究会发现这个函数都不需要定义,只需要声明就可以了,此为后话.
因为模板的原因,必须把生命和定义都写在hpp里面,这样会导致1.obj和2.obj都会有一个相同的定义,所以会报最初的那个错误,尝试了下,这么写就不会报错:
void f();
// .cpp
void f()
... {
BOOST_MPL_ASSERT_MSG(sizeof Obj_T == sizeof Src_T, obj_type_size_is_not_same_with_src_type, (Obj_T, Src_T));
}
但是模板不能够分开写,或许这样可以解决问题
... {
template <typename Obj_T, typename Src_T>
Obj_T union_cast(Src_T src)
...{
// 编译期检查Src_T和Obj_T的数据大小是一致。
//
BOOST_MPL_ASSERT_MSG(sizeof Obj_T == sizeof Src_T, obj_type_size_is_not_same_with_src_type, (Obj_T, Src_T));
union
...{
Src_T m_src;
Obj_T m_obj;
} tmp_union;
tmp_union.m_src = src;
return tmp_union.m_obj;
}
}
bingo!!!!!!!解决了,注意要使用匿名名字空间哦,这样在1.obj和2.obj里面会生成完全不同的符号,因此不会发生冲突,如果使用具名的命名空间就又会冲突哦,对于匿名名字空间里面函数,对于cpp来说,可以无视命名空间,也就是说以前的使用代码完全不用更改,直接使用,完美的解决方案:)
====================================================================================
2007年3月9日,继续更新
原来以为是完美的解决方案,结果又出了新的问题,因为在不同的cpp里面的话,匿名namespace相当于不同的命名空间,所以在cpp里面会生成不同的代码,那么如果在union_cast里面出现static int x这样的变量也会产生两份,因此完全背离了定义static的意义.另外因为在匿名空间里面,没有办法把一个类的定义和声明分开来写,所以,不完美!!!!!!!那么继续深究,到底是什么导致编译冲突
// 1.cpp
static void f()
... {
class A
...{
static void fff()
...{
}
};
}
// 2.cpp
static void f()
... {
class A
...{
static void fff()
...{
}
};
}
结果编译的时候两个obj里面的fff()函数冲突了,难道是static的函数的问题,继续测试:
static void f()
... {
class A
...{
void fff()
...{
}
};
}
// 2.cpp
static void f()
... {
class A
...{
void fff()
...{
}
};
}
继续连接冲突,晕死
class A
... {
static void fff()
...{
}
} ;
// 2.cpp
class A
... {
static void fff()
...{
}
} ;
这样就不会冲突,我把上面两个会冲突的写法,用gcc来编译,就可以顺利编过,因此我认为这是一个vc的bug,事实我要接受,我也不可能转去gcc,来想想解决办法吧
转回来看BOOST_MPL_ASSERT_MSG的实现,这里再贴一次
struct msg;
typedef struct BOOST_PP_CAT(msg,__LINE__) : boost::mpl::assert_
... {
static boost::mpl::failed ************ (msg::************ assert_arg()) types_
...{ return 0; }
} BOOST_PP_CAT(mpl_assert_arg,__LINE__);
enum ... {
BOOST_PP_CAT(mpl_assertion_in_line_,__LINE__) = sizeof(
boost::mpl::assertion_failed<(c)>( BOOST_PP_CAT(mpl_assert_arg,__LINE__)::assert_arg() )
)
}
下面这个
enum ...{
BOOST_PP_CAT(mpl_assertion_in_line_,__LINE__) = sizeof(
boost::mpl::assertion_failed<(c)>( BOOST_PP_CAT(mpl_assert_arg,__LINE__)::assert_arg() )
)
目的就是为了sizeof,sizeof不会执行函数,只关心函数的返回类型,所以assert_arg()有没有实现都是没有问题的,因此可以修改为
struct msg;
typedef struct BOOST_PP_CAT(msg,__LINE__) : boost::mpl::assert_
... {
static boost::mpl::failed ************ (msg::************ assert_arg()) types_ ;
} BOOST_PP_CAT(mpl_assert_arg,__LINE__);
enum ... {
BOOST_PP_CAT(mpl_assertion_in_line_,__LINE__) = sizeof(
boost::mpl::assertion_failed<(c)>( BOOST_PP_CAT(mpl_assert_arg,__LINE__)::assert_arg() )
)
}
这样问题可以得到解决,但是会报一个warning:C4822 : local class member function does not have a body,还是不完美啊,继续.大家知道纯虚函数是不需要实现的,但是必须在对象上实现,但是纯虚的对象是无法定义的,灵光一闪,黄金万两,反正不会真正调用,只要语法过了就对了,那么我们可以在一个null指针上面调用,最终版本就像下面:
struct msg;
typedef struct BOOST_PP_CAT(msg,__LINE__) : boost::mpl::assert_
... {
virtual boost::mpl::failed ************ (msg::************ assert_arg()) types_ = 0;
} BOOST_PP_CAT(mpl_assert_arg,__LINE__);
enum ... {
BOOST_PP_CAT(mpl_assertion_in_line_,__LINE__) = sizeof(
boost::mpl::assertion_failed<(c)>( BOOST_PP_CAT(static_cast<mpl_assert_arg,__LINE__)*>(0)->assert_arg() )
)
}
完美解决,这次发现了boost一个bug,vc2005(vc2003也有)一个bug.