神奇的BOOST_MPL_ASSERT_MSG,神奇的解决方案,神奇的vc的bug

运行环境VC2005.

工程里面写了这么一段代码,目的是为了实现DWORD<==>DWORD*之间完成转型而不报warning

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的特性,所以要保证转型的变量大小要相等

    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)里面使用就出现问题了,会报如下联结错误:

1 .obj : error LNK2005:  " public: static struct boost::mpl::failed * * * * * * * * * * * * (__thiscall `unsigned long * __cdecl union_cast<unsigned long *,unsigned long>(unsigned long)'::`2'::obj_type_size_is_not_same_with_src_type::** * * * * * * * * * * __cdecl `unsigned long * __cdecl union_cast<unsigned long *,unsigned long>(unsigned long)'::`2'::obj_type_size_is_not_same_with_src_type15::assert_arg(void))(unsigned long *,unsigned long) "  ( ? assert_arg@obj_type_size_is_not_same_with_src_type15@ ? 1 ??? $union_cast@PAKK@@YAPAKK@Z@SAPAPAPAPAPAPAPAPAPAPAPAP8obj_type_size_is_not_same_with_src_type@ ? 1 ??? $union_cast@PAKK@@YAPAKK@Z@AEPAPAPAPAPAPAPAPAPAPAPAPAUfailed@mpl@boost@@PAKK@ZXZ) already defined  in   2 .obj

看着比较恐怖,但是实际上就是说union_cast里面assert_arg函数被重复定义了,奇怪了,我又没有定义过assert_arg这个函数,那肯定是BOOST_MPL_ASSERT_MSG这个宏干的事情了(这个宏实际上完成的工作是,编译期的assert),查看下这个宏的定义,是这么写的:

#   define BOOST_MPL_ASSERT_MSG( c, msg, types_ ) 
    
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() ) 
            ) 
    }

哈,凶手出来了,宏展开之后会定义一个函数

static  boost::mpl::failed  ************  (msg:: ************  assert_arg()) types_
        
return 0; }  

不要被这个函数吓倒,我把它分开来写,或许你就知道了:

typedef boost::mpl::failed  ************  (msg:: ************  _ret) types_

static  _ret assert_arg() return 0; }

虽然有那么多*,但是不过就是个函数指针,*不过是为了出错信息更容易看出来,毕竟模板出错信息返回一大堆,return 0就是个NULL指针,其实后面继续研究会发现这个函数都不需要定义,只需要声明就可以了,此为后话.

因为模板的原因,必须把生命和定义都写在hpp里面,这样会导致1.obj和2.obj都会有一个相同的定义,所以会报最初的那个错误,尝试了下,这么写就不会报错:

// .h
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));
}

但是模板不能够分开写,或许这样可以解决问题

namespace
{
    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的函数的问题,继续测试:

// 1.cpp
static   void  f()
{
    
class A
    
{
        
void fff()
        
{
        }

    }
;
}


// 2.cpp
static   void  f()
{
    
class A
    
{
        
void fff()
        
{
        }

    }
;
}

继续连接冲突,晕死

// 1.cpp
class  A
{
    
static void fff()
    
{
    }

}
;

// 2.cpp
class  A
{
    
static void fff()
    
{
    }

}
;

这样就不会冲突,我把上面两个会冲突的写法,用gcc来编译,就可以顺利编过,因此我认为这是一个vc的bug,事实我要接受,我也不可能转去gcc,来想想解决办法吧

转回来看BOOST_MPL_ASSERT_MSG的实现,这里再贴一次

#   define BOOST_MPL_ASSERT_MSG( c, msg, types_ ) 
    
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()有没有实现都是没有问题的,因此可以修改为

#   define BOOST_MPL_ASSERT_MSG( c, msg, types_ ) 
    
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指针上面调用,最终版本就像下面:

 

#   define BOOST_MPL_ASSERT_MSG( c, msg, types_ ) 
    
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.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值