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(); \
        } \

posted on 2016-08-21 23:41  冷欺花  阅读(1657)  评论(0编辑  收藏  举报

导航