UE4 Slate 特殊语法

UE Slate 特殊语法分析

简述

UE4 的Slate系统有很多奇怪的语法,创建新的SWidget对象时,语法中会包含SNew,.XXX() , +XXX 和 [XXX]类型的语法 。
在自定义SWidget类的时候,会需要用到SLATE_BEGIN_ARGS , SLATE_END_ARGS等语法,并且需要实现Construct方法,
这些特殊的语法和规则,都是UE4通过宏定义,运算符重载来完成的。UE4希望尽可能简化Slate相关的代码编写难度,
但是对于不熟悉“内幕”的人来说,这些特殊语法可能会造成一些困惑。这里尝试去分析Slate所有的特殊语法,了解这些特殊语法
是怎么起作用的,写Slate相关代码时需要遵守的各种“潜规则”又是怎么来的。

从SNew开始

一个最简单的创建SWidget的代码大致如下

    SNew(STextBlock).Text(FText::FromString("Hello World"));

几乎所有的Slate相关宏,都可以在 SlateCore\Public\Widgets\DeclarativeSyntaxSupport.h 中找到。
其中,SNew的定义如下:

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

TYPENAME_OUTSIDE_TEMPLATE定义为

    #define TYPENAME_OUTSIDE_TEMPLATE	typename

把这个东西直接忽略掉似乎也没事

那么,把最开始的代码,宏展开,去掉typename修饰,假定当前的__FILE__是Source.cpp, __LINE__是20(这是SNew代码出现的位置,Debug用的)
大概就成了下面这个样子:

    MakeTDecl<STextBlock>("STextBlock","Source.cpp",20,RequiredArgs::MakeRequiredArgs())
    <<=
    STextBlock::FArguments().Text(FText::FromString("Hello World"));

分析一下这行代码的执行顺序,C++代码的执行顺序,具体顺序规则相当复杂1,不过应该会遵守以下规则(个人总结):

  1. 如果需要执行某个函数,那么必定会先对所有参数求值(如果参数是函数,那么参数对应的函数必定会先执行)
  2. XXX.YYY().ZZZ()类型的多个函数连续调用,必定是先调用左边的函数,再用它的返回值去调用右边的函数
  3. 如果有多个运算符号都可以执行,那么优先级高的运算符先执行

由于C++中 <<= 预算符的优先级相当低,所以上面代码的执行顺序大概可以认为是:

  1. 先执行 <<= 符号左边的表达式 MakeTDecl(…)
  2. 再执行 <<= 符号右边的表达式 STextBlock::FArguments().xxx
  3. 最后,执行 <<= 预算符(这个运算符被重载了),返回最终的SWidget对象(智能指针)

编译器可能选择不严格按照这个顺序执行代码,但是最终结果应该与这个顺序的结果一致 (之后的执行顺序分析不再赘述这一点)

已经可以发现,实际的代码执行和看上去的并不一致,在使用SNew创建SWidget的时候,跟在SNew后面的一系列 .XXX().YYY()函数,
看上去就像是在调用SWidget对象的函数,更改它的属性。然而,实际上这些函数是在另外的对象上调用的,并没有直接修改SWidget对象的数据。
(当然,最终能生效,说明肯定还是间接更改SWidget对象的属性了,只不过更改过程不想看上去那么直接)

更具体的分析一下一上代码的执行顺序:

  1. RequiredArgs::MakeRequiredArgs() 方法被调用,这是一个泛型方法,根据传入参数的不同,会调用到不同的方法,最终生成
    T0RequiredArgs – T5RequiredArgs的对象并返回,这个对象的作用,可以认为是存储SNew中,除了类名之外的其它参数,最多可以有五个
  2. MakeTDecl 方法被调用,生成一个TDecl的对象,这个对象包含真正的SWidget对象。TDecl对象,在构造方法里面,会负责
    对应SWidget对象的创建。这个对象的创建不是直接通过new方法来生成的,而是通过专门的Allocator,UE可能在内部使用了
    内存池的技术,减少不必要的对象生成。

    此时,虽然对应的SWidget对象已经生成了,但是对应的Construct方法并没有调用,所以这个SWidget对象还是没有真正初始化的。

    另外,TDecl的构造方法中,会将SWidget对象的Debug信息(SNew的文件名,行号,SWidget的名称)赋值给这个SWidget对象。
  3. STextBlock::FArguments()被调用,生成STextBlock::FArguments对象,这个对象用来执行接下来的.Text()方法
  • 10
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值