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(); \
} \
ue4 SNew补遗的更多相关文章
- ue4 SNew背后的逻辑
ue4的ui库Slate体系非常庞大,即使是在创建对象这一小事上,也是相当复杂: SLATECORE_API TSharedRef<SWidget> SNullWidget::NullWi ...
- Houdini技术体系 基础管线(四) :Houdini驱动的UE4植被系统 下篇
背景 在上篇中,实现了使用Houdini在UE4里根据地形过程生成植被的最基本的原型.并且支持把植被在UE4里Bake成使用的HierarchicalInstancedStaticMeshCompon ...
- UE4之Slate:App默认窗口的创建流程
UE4版本:4.24.3源码编译 Windows10 + VS2019开发环境 在先前分享的基础上,现在来梳理下App启动时默认窗口的创建流程,以及相关的类.对象之间的抽象层级: 纯C++工程配置 S ...
- UE4之Slate: SImage
概述 距离上次记录<UE4之Slate:纯C++工程配置>后已经好长时间了: 这个随笔来记录并分享一下SImage控件的使用,以在屏幕上显示一张图片: 目标 通过SImage控件的展示,学 ...
- UE4自动打包工具编写
在UE的开发中,有些项目需要针对不同版本出不同的包,并有一个对应的GUI界面,供大家使用. 1.插件编写 先使用UE4自己的插件模板创建插件,做成插件形式 然后注册Slate UI,编写打开逻辑.并在 ...
- UE4新手引导之下载和安装虚幻4游戏引擎
1) 进入虚幻4的官方主页(https://www.unrealengine.com/) 这里你可以获得关于虚幻4的最新资讯,包括版本更新.博客更新.新闻和商城等.自2015年起,该引擎已经提供免费下 ...
- UE4新手引导入门教程
请大家去这个地址下载:file:///D:/UE4%20Doc/虚幻4新手引导入门教程.pdf
- ue4 c++学习推荐
我由易到难推荐,不过在此之前还是先看看官方对于VS设置的推荐: https://docs.unrealengine.com/latest/INT/Programming/Development/Vis ...
- 如何创建独立的UE4服务端
原文作者:@玄冬Wong 转载请注明原文出处:http://aigo.iteye.com/blog/2268777 这是论坛上对UE服务端功能的回答,意思是UE4提供了主流MMO网游服务端所具备的特性 ...
随机推荐
- ASP.NET MVC IOC 之AutoFac攻略
一.为什么使用AutoFac? 之前介绍了Unity和Ninject两个IOC容器,但是发现园子里用AutoFac的貌似更为普遍,于是捯饬了两天,发现这个东东确实是个高大上的IOC容器~ Autofa ...
- 核心动画和UIView动画的区别
核心动画和UIView动画的区别 1.核心动画制作用在Layer 2.核心动画的修改的属性都是假象,他的真实位置没有发生变化()
- BeanFactory 和ApplicationContext(Bean工厂和应用上下文)
Bean工厂(BeanFactory接口),提供了基础的依赖注入支持. 应用上下文(ApplicationContext接口),建立在Bean工厂基础之上,提供了系统架构服务. Application ...
- Linux进程切换代码分析
朱宇轲 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 这次我们来分析L ...
- 数据库 MySql(二)
MySQL(二) 1.外键及连表 外键 一个特殊的索引,只能是指定内容 CREATE TABLE part1 ( nid INT NOT NULL auto_increment PRIMARY KEY ...
- Debian 7.6 新编译内核 3.15.6 开机加载黑屏
需要手动加载 fbcon 这个模块,或者编译内核的时候,Framebuffer Console support 编译进内核(后者没测试过).加在模块只要修改/etc/default/grub文件或者/ ...
- Windows下利用Windbg 分析dump
概述: 注册生成dump文件的函数. 当程序收到没有捕获的异常时,调用上述函数,生成dump文件. 利用Windbg结合编译程序时生成的pdb和代码来分析dump文件,定位问题. 如下代码生成dump ...
- [Android]通过js方法回调部分native报错 Web Console: Uncaught TypeError: Object [object Object] has no method 'xxx'
在android4.2以前,注入步骤如下: webview.getSetting().setJavaScriptEnable(true); class JsObject { public String ...
- C#winform如何最小化主窗口
1.如果不想让程序在任务栏中显示,请把窗体的属性ShowInTaskbar设置为false;2.如果想让程序启动时就最小化,请设置窗体的属性WindowState设置为Minimized.(Minim ...
- easyui datagrid 跨页选择
$.fn.extend( memberList ,{ quickSearch : function() { var time1 = new Date(); /* this.datagrid.datag ...