zoukankan      html  css  js  c++  java
  • ue4 SNew补遗

    上一篇分析了SNew背后的实现,但是有一个关键问题遗漏了,那就是:

    #define SNew( WidgetType, ... ) 
        MakeTDecl<WidgetType>( #WidgetType, __FILE__, __LINE__, RequiredArgs::MakeRequiredArgs(__VA_ARGS__) ) <<= TYPENAME_OUTSIDE_TEMPLATE WidgetType::FArguments()

    为何这里要用一个特别奇怪的操作符重载:【<<=】??

    这个宏的目的无非是包装了WidgetType实例的创建和初始化,为何不用一个InitFromArg(FArguments& arg)之类的常规成员函数呢?

    要解释这个问题,可以看下面一段示例代码:

      TSharedRef<SOverlay> ViewportOverlayWidgetRef = SNew( SOverlay );
    
        TSharedRef<SGameLayerManager> GameLayerManagerRef = SNew(SGameLayerManager)
            .SceneViewport_UObject(this, &UGameEngine::GetGameSceneViewport, GameViewportClient)
            .UseScissor(false)
            [
                ViewportOverlayWidgetRef
            ];

    上面两句代码,都是用SNew创建一个新对象,一个不带参数,一个带了一串链式调用,要支持后者的写法,就是使用【<<=】的目的。

    首先,要看清楚这一串链式调用所作用的对象:是FArgment而非WidgetType

    三个调用:SceneViewport_UObject、UseScissor和operator[],分别是用SLATE_ATTRIBUTE、SLATE_ARGUMENT、SLATE_DEFAULT_SLOT三个宏生成的。

    然而更重要的是它们都包在SLATE_BEGIN_ARGS/SLATE_END_ARGS之间,而这一对宏就是在WidgetType类里再定义一个内嵌类:

    #define SLATE_BEGIN_ARGS( WidgetType ) 
        public: 
        struct FArguments : public TSlateBaseNamedArgs<WidgetType> 
        { 
            typedef FArguments WidgetArgsType; 
            FORCENOINLINE FArguments()
    
    #define SLATE_END_ARGS() 
        };

    这就是说链式调用不可能发生成SNew返回的Widget实例上,如果SNew真的是先返回一个Widget,那代码就大大有误了,连编译都过不了。

    然而事实是一切顺利,那上述矛盾如何解决的呢?回过头看一下SNew展开那句代码的最后,是没有分号【;】的!这就给SNew()后面的代码提供了一个奇妙的组合结果!

    就比如说下面这句话:

    SNew(SGameLayerManager).UseScissor(false);
    

    展开后成为:(简化)

    MakeTDecl<SGameLayerManager>() <<= SGameLayerManager::FArguments().UseScissor(false);

    一下子就高能了!因为操作符【<<=】的优先级是非常低的,这将导致它后面的代码被先结合起来,运算完了之后再整体做为【<<=】的参数传给前方的Widget实例。

    而结合上面提到的链式调用都会返回this,所以后面不管跟多少调用,最终得到的结果都还是一个FArgument对象,恰恰是【<<=】所需要的类型。

    此处的巧妙就在于,做为原本在同一个宏(SNew)里定义的三段:Widget <<= FArgument,在最终代码里并没有形成一体,而是利用:

    1、<<=的低级先级

    2、末尾没带分号

    使FArgument与之后的代码优先结合,完成了一连串的链式调用后,再做为整体返还。

    现在回到最初的问题:如果不使用操作符重载,而是一个普通的InitFromArg函数,会发生什么情形:

    MakeTDecl<WidgetType>(SGameLayerManager).InitFromArg(SGameLayerManager::FArguments()).UseScissor(false);

    显然不可能再优先后结合了。

    附:SLATE_ATTRIBUTE

    SLATE_ATTRIBUTE展开后,生成一个TAttribute< AttrType >成员,及大量对此属性赋值的方法,而此例中带_UObject后缀的版本,则是将其绑定到一个UObject类的成员函数上:

    #define SLATE_ATTRIBUTE( AttrType, AttrName ) 
            TAttribute< AttrType > _##AttrName; 
    
         template< class UserClass, typename Var1Type >    
            WidgetArgsType& AttrName##_UObject( UserClass* InUserObject, typename TAttribute< AttrType >::FGetter::template TUObjectMethodDelegate_OneVar_Const< UserClass, Var1Type >::FMethodPtr InFunc, Var1Type Var1 )    
            { 
                _##AttrName = TAttribute< AttrType >::Create( TAttribute< AttrType >::FGetter::CreateUObject( InUserObject, InFunc, Var1 ) ); 
                return this->Me(); 
            } 
  • 相关阅读:
    接口调试之Postman 使用方法详解
    用Vue2仿京东省市区三级联动效果
    高德地图JS API获取经纬度,根据经纬度获取城市
    js 格式化数字,格式化金额:
    CSS Media媒体查询使用大全,完整媒体查询总结
    最新手机号正则表达式 java 、javascript版正则表达式验证是否为11位有效手机号码
    JavaScript 实现textarea限制输入字数, 输入框字数实时统计更新,输入框实时字数计算移动端bug解决
    在上线项目中,用Vue写一个星级评价
    new Date()设置日期在IOS的兼容问题
    javascript 省市区三级联动 附: json数据
  • 原文地址:https://www.cnblogs.com/wellbye/p/5787867.html
Copyright © 2011-2022 走看看