上一篇分析了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(); \
} \

ue4 SNew补遗的更多相关文章

  1. ue4 SNew背后的逻辑

    ue4的ui库Slate体系非常庞大,即使是在创建对象这一小事上,也是相当复杂: SLATECORE_API TSharedRef<SWidget> SNullWidget::NullWi ...

  2. Houdini技术体系 基础管线(四) :Houdini驱动的UE4植被系统 下篇

    背景 在上篇中,实现了使用Houdini在UE4里根据地形过程生成植被的最基本的原型.并且支持把植被在UE4里Bake成使用的HierarchicalInstancedStaticMeshCompon ...

  3. UE4之Slate:App默认窗口的创建流程

    UE4版本:4.24.3源码编译 Windows10 + VS2019开发环境 在先前分享的基础上,现在来梳理下App启动时默认窗口的创建流程,以及相关的类.对象之间的抽象层级: 纯C++工程配置 S ...

  4. UE4之Slate: SImage

    概述 距离上次记录<UE4之Slate:纯C++工程配置>后已经好长时间了: 这个随笔来记录并分享一下SImage控件的使用,以在屏幕上显示一张图片: 目标 通过SImage控件的展示,学 ...

  5. UE4自动打包工具编写

    在UE的开发中,有些项目需要针对不同版本出不同的包,并有一个对应的GUI界面,供大家使用. 1.插件编写 先使用UE4自己的插件模板创建插件,做成插件形式 然后注册Slate UI,编写打开逻辑.并在 ...

  6. UE4新手引导之下载和安装虚幻4游戏引擎

    1) 进入虚幻4的官方主页(https://www.unrealengine.com/) 这里你可以获得关于虚幻4的最新资讯,包括版本更新.博客更新.新闻和商城等.自2015年起,该引擎已经提供免费下 ...

  7. UE4新手引导入门教程

    请大家去这个地址下载:file:///D:/UE4%20Doc/虚幻4新手引导入门教程.pdf

  8. ue4 c++学习推荐

    我由易到难推荐,不过在此之前还是先看看官方对于VS设置的推荐: https://docs.unrealengine.com/latest/INT/Programming/Development/Vis ...

  9. 如何创建独立的UE4服务端

    原文作者:@玄冬Wong 转载请注明原文出处:http://aigo.iteye.com/blog/2268777 这是论坛上对UE服务端功能的回答,意思是UE4提供了主流MMO网游服务端所具备的特性 ...

随机推荐

  1. Unity3D使用Assetbundle打包加载(Prefab、场景)

    之前有一篇文章中我们相惜讨论了Assetbundle的原理,如果对原理还不太了解的朋友可以看这一篇文章:Unity游戏开发使用Assetbundle加载场景的原理 本篇文章我们将说说assetbund ...

  2. Win32API界面库 - Project wheels 工程基础部分完成

    离上次发博文过去了好久,先是要忙一个机器人的项目,然后就是部门的事情和考试周复习,然后就到了考试周,趁着复习的间隙,拾起了寒假时候抄的界面库,修掉了从前的bug. bug1 控件显示问题 当初抄这个库 ...

  3. string.Format之你不知道的事

    1.格式化货币(跟系统的环境有关,中文系统默认格式化人民币,英文系统格式化美元) string.Format("{0:C}",0.2) 结果为:¥0.20 (英文操作系统结果:$0 ...

  4. web中session与序列化的问题

    最近在写网上商城项目的时候学习了一个关于session的序列化问题,过来总结一下. 众所周知,session是服务器端的一种会话技术,只要session没有关闭,一个会话就会保持.这里先引出一个问题: ...

  5. SG函数闲扯(转)

    http://ydcydcy1.blog.163.com/blog/static/216089040201342412717440/ 没来得及看.

  6. Node以数据块的形式读取文件

    在Node中,http响应头信息中Transfer-Encoding默认是chunked. Transfer-Encoding:chunked Node天生的异步机制,让响应可以逐步产生. 这种发送数 ...

  7. 【练习】flushback基于时间的闪回查询

    1.创建table t1 :: SCOTT@ORA11GR2>create table t1 as select * from scott.emp; Table created. :: SCOT ...

  8. 解决box-flex不均等分的问题

    我想当你上手css3的时候后一定为他的强大而感到震惊,但是震惊之后带来的一定是苦恼,因为他太TM变态了! 我之所以这么说是因为我今天写box-flex的时候遇到了一个可以让我蛋碎的问题~~~ 首先,b ...

  9. tcp协议-http协议-time-wait-close-wait必知

     前言:  tcp四次挥手过程中,谁主动断开,谁有time_wait,被动断开一方会有close_wait time_wait:保持端口占用2mls~4min,避免对方还有一些tcp片发往这个端口,新 ...

  10. Spring项目解决Post乱码

    Java EE解决Post乱码:在web.xml中加入: <filter> <filter-name>encodingFilter</filter-name> &l ...