반응형
SLATE_ATTRIBUTE 와 TAttribute 에 대해서 알아보겠습니다




먼저 언리얼 내부를 보다보면 SLATE_ATTRIBUTE  라는 것을 볼 수있다 그런데 이 매크로를 보면 다음과 같습니다



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define SLATE_ATTRIBUTE( AttrType, AttrName ) \
        TAttribute< AttrType > _##AttrName; \
        WidgetArgsType& AttrName( const TAttribute< AttrType >& InAttribute ) \
        { \
            _##AttrName = InAttribute; \
            return this->Me(); \
        } \
    \
        /* Bind attribute with delegate to a global function
         * NOTE: We use a template here to avoid 'typename' issues when hosting attributes inside templated classes */ \
        template< typename StaticFuncPtr > \
        WidgetArgsType& AttrName##_Static( StaticFuncPtr InFunc )    \
        { \
            _##AttrName = TAttribute< AttrType >::Create( TAttribute< AttrType >::FGetter::CreateStatic( InFunc ) ); \
            return this->Me(); \
        } \
        template< typename Var1Type > \
        WidgetArgsType& AttrName##_Static( typename TAttribute< AttrType >::FGetter::template TStaticDelegate_OneVar< Var1Type >::FFuncPtr InFunc, Var1Type Var1 )    \
        { \
            _##AttrName = TAttribute< AttrType >::Create( TAttribute< AttrType >::FGetter::CreateStatic( InFunc, Var1 ) ); \
            return this->Me(); \
        } \
cs


이 안에  TAttribute 라는 변수를 생성하고 있습니다

그런데 언뜻 보면   Slate_Attribute 매크로에서 이 TAttribute 타입 변수에 별의별 타입을 다 담는것 처럼 보여서 이것의 정체가 무엇인지
혼동 할 수가 있는데 이것은 함수와 인자를 담는 델리게이트의 역할을 같이 하고 있습니다

또한 델리게이트를 생성하는 방법은 다양합니다 함수를 델리게이트에 담는 방법, reference count 가 있는 shared 포인터와 연관된 
델리게이트 등 다양하게 델리게이트와 연관 될 수 있게 설계 되어 있습니다



example..
1
2
3
4
5
6
template< class UserClass, typename Var1Type >    \
WidgetArgsType& AttrName( TSharedRef< UserClass > InUserObjectRef, 
typename TAttribute< AttrType >::FGetter::template TSPMethodDelegate_OneVar_Const< UserClass, Var1Type >::FMethodPtr InFunc, 
Var1Type Var1 )    \
{ \
     _##AttrName = TAttribute< AttrType >::Create( TAttribute< AttrType >::FGetter::CreateSP( InUserObjectRef, InFunc, Var1 ) ); \
     return this->Me(); \
} \
cs




정리하자면 

언리얼 내부에 TAttribute 라는 template  클래스를 볼 수 가 있는데 

예를 들어 TAttribute<bool>  해주면 bool 이라는 변수 타입을 갖는 변수가 하나 생성 되고, 
또한 bool 변수에 대해 처리하고자 하는 바인딩 함수를 걸 수 있습니다

변수 타입의 확장형이라고 볼 수 있습니다 (확장이란 말이 약간 오해의 소지가 있을 수 있지만 코드를 보면 무슨 의미인지 아실 수 있을 겁니다)

SLATE_ATTRIBUTE 는 곧 변수와  델리게이트를 갖는 TAttribute 를 만들고 생성시 지정한 AttrName 에 델리게이트를 담는 함수를
define 한 매크로이다

Tip : 
또 매크로가 있는 특성상 지정한 이름에 뒤에 _Static 등을 붙여 함수로 만들어 치환 하는 경우가 많아서 그냥 소스를 보다가 갑자기
듣보 보도 못한 함수나 변수가 나오는게 이러한 이유때문이다, 자세한건 아래 소스를 참고하면 됩니다





아래 분석 구문을 보면.. 중간 중간 주석을 달아 놓았습니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#define FUNC_DECLARE_DELEGATE( DelegateName, ... ) \
 
    typedef TBaseDelegate<__VA_ARGS__> DelegateName;
 
// 이것이 delegate 생성기, 함수 등을 bind 할 수 있다
 
 
 


 
#define DECLARE_DELEGATE_RetVal( ReturnValueType, DelegateName ) FUNC_DECLARE_DELEGATE( DelegateName, ReturnValueType )
 
 

 


 
/*예로
아래 구문 중 이런 구문이 있다
    static TAttribute Create( typename FGetter::FStaticDelegate::FFuncPtr InFuncPtr )
    {
        const bool bExplicitConstructor = true;
        return TAttribute( FGetter::CreateStatic( InFuncPtr ), bExplicitConstructor );                //이 것이 델리게이트에 InFuncPtr 함수를 바인딩 하면서 생성 하는 과정
    }
FGetter 는 아래 정의된 델리게이트인데 , 델리게이트 FGetter 를 통해서 CreateStatic 함수로 InFuncPtr이라는 수를 바인딩해서 만든 델리게이트를 리턴해
TAttribute 를 생성한다
 
*/





//예를 들어 TAttribute<bool>  해주면 bool 이라는 변수 타입을 갖는 변수가 하나 생성 되고, 
//또한 bool 변수에 대해 처리하고자 하는 바인딩 함수를 걸 수 있다
/**
 * Attribute object
 */
template< typename ObjectType >
class TAttribute
{
public:
    /**
     * Attribute 'getter' delegate
     *
     * ObjectType GetValue() const
     *
     * @return  The attribute's value
     */
    DECLARE_DELEGATE_RetVal( ObjectType, FGetter );
    /** Default constructor. */
    TAttribute()
        : Value()         // NOTE: Potentially uninitialized for atomics!!
        , bIsSet(false)
        , Getter()
    { }
    /**
     * Construct implicitly from an initial value
     *
     * @param  InInitialValue  The value for this attribute
     */
    template< typename OtherType >
    TAttribute( const OtherType& InInitialValue )
        : Value( InInitialValue )        
        , bIsSet(true)
        , Getter()
    { }
........
중략
........
    /**
     * Static: Creates an attribute that's pre-bound to the specified 'getter' delegate
     *
     * @param  InGetter        Delegate to bind
     */
    static TAttribute Create( const FGetter& InGetter )
    {
        const bool bExplicitConstructor = true;
        return TAttribute( InGetter, bExplicitConstructor );
    }
    
    /**
     * Creates an attribute by binding an arbitrary function that will be called to generate this attribute's value on demand. 
     * After binding, the attribute will no longer have a value that can be accessed directly, and instead the bound
     * function will always be called to generate the value.
     *
     * @param  InFuncPtr Member function to bind.  The function's structure (return value, arguments, etc) must match IBoundAttributeDelegate's definition.
     */
    static TAttribute Create( typename FGetter::FStaticDelegate::FFuncPtr InFuncPtr )
    {
//이 것이 델리게이트에 InFuncPtr 함수를 바인딩 하면서 생성 하는 과정
        const bool bExplicitConstructor = true;
        return TAttribute( FGetter::CreateStatic( InFuncPtr ), bExplicitConstructor );   
    }
    /**
     * Sets the attribute's value
     *
     * @param  InNewValue  The value to set the attribute to
     */
    template< typename OtherType >
    void Set( const OtherType& InNewValue )
    {
        Getter.Unbind();
        Value = InNewValue;
        bIsSet = true;
    }
    /** Was this TAttribute ever assigned? */
    bool IsSet() const
    {
        return bIsSet;
    }
    /**
     * Binds an arbitrary function that will be called to generate this attribute's value on demand.
     * After binding, the attribute will no longer have a value that can be accessed directly, and instead the bound
     * function will always be called to generate the value.
     *
     * @param  InUserObject  Instance of the class that contains the member function you want to bind.
     * @param  InFunctionName Member function name to bind.
     */
    template< class SourceType >
    void BindUFunction( SourceType* InUserObject, const FName& InFunctionName )
    {
        bIsSet = true;
        Getter.BindUFunction(InUserObject, InFunctionName);
    }
    /**
     * Creates an attribute by binding an arbitrary function that will be called to generate this attribute's value on demand.
     * After binding, the attribute will no longer have a value that can be accessed directly, and instead the bound
     * function will always be called to generate the value.
     *
     * @param  InUserObject  Instance of the class that contains the member function you want to bind.
     * @param  InFunctionName Member function name to bind.
     */
    template< class SourceType >
    static TAttribute< ObjectType > Create(SourceType* InUserObject, const FName& InFunctionName)
    {
        TAttribute< ObjectType > Attrib;
        Attrib.BindUFunction<SourceType>(InUserObject, InFunctionName);
        return Attrib;
    }
........
중략
........

    /**
     * Is this attribute identical to another TAttribute.
     *
     * @param InOther The other attribute to compare with.
     * @return true if the attributes are identical, false otherwise.
     */
    bool IdenticalTo(const TAttribute& InOther) const
    {
        const bool bIsBound = IsBound();
        if ( bIsBound == InOther.IsBound() )
        {
            if ( bIsBound )
            {
                return Getter.GetHandle() == InOther.Getter.GetHandle();
            }
            else
            {
                return Value == InOther.Value;
            }
        }
        return false;
    }
........
중략
........
private:
    /** Special explicit constructor for TAttribute::Create() */
    TAttribute( const FGetter& InGetter, bool bExplicitConstructor )
        : Value()        
        , bIsSet( true )
        , Getter(InGetter)
    { }
    // We declare ourselves as a friend (templated using OtherType) so we can access members as needed
    template< class OtherType > friend class TAttribute;    


    /** Current value.  Mutable so that we can cache the value locally when using a bound Getter (allows const ref return value.) */
    mutable ObjectType Value;            //이건 이전에 이전에 지정한 타입에 대한 변수이고


    /** true when this attribute was explicitly set by a consumer, false when the attribute's value is set to the default*/
    bool bIsSet;
    /** Bound member function for this attribute (may be NULL if no function is bound.)  When set, all attempts
        to read the attribute's value will instead call this delegate to generate the value. */
    /** Our attribute's 'getter' delegate */
    FGetter Getter;                        //그래서 이게 delegate 가 됨
};
----------------------------------------------------------------------------------------
/**
 * Use this macro to add a attribute to the declaration of your widget.
 * An attribute can be a value or a function.
 */
#define SLATE_ATTRIBUTE( AttrType, AttrName ) \
        TAttribute< AttrType > _##AttrName; \
        WidgetArgsType& AttrName( const TAttribute< AttrType >& InAttribute ) \
        { \
            _##AttrName = InAttribute; \
            return this->Me(); \
        } \
    \
    ......
//some example..
SLATE_ATTRIBUTE( FText, ToolTipText )
SLATE_ATTRIBUTE( TOptional<EMouseCursor::Type>, Cursor )
SLATE_ATTRIBUTE( bool, IsEnabled )
SLATE_ATTRIBUTE( EVisibility, Visibility )
SLATE_ATTRIBUTE( TOptional<FSlateRenderTransform>, RenderTransform )
SLATE_ATTRIBUTE( FVector2D, RenderTransformPivot )
cs




그렇다면 왜 Slate_ATTRIBUTE 를 만들언 놓은 것인가 왜 델리게이트와 엮이는 매크로를 만들어 놓은 것인가..?




다음 구문을 한번 봅시다


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/* Base class for named arguments. Provides settings necessary for all widgets. */
template<typename WidgetType>
struct TSlateBaseNamedArgs
{
    typedef typename WidgetType::FArguments WidgetArgsType;
    
    TSlateBaseNamedArgs()
    : _ToolTipText()
    , _ToolTip()
    , _Cursor( TOptional<EMouseCursor::Type>() )
    , _IsEnabled( true )
    , _Visibility( EVisibility::Visible )
    , _RenderTransform( )
    , _RenderTransformPivot( FVector2D::ZeroVector )
    , _ForceVolatile( false )
    {
    }
 
    /** Used by the named argument pattern as a safe way to 'return *this' for call-chaining purposes. */
    WidgetArgsType& Me()
    {
        return *(static_cast<WidgetArgsType*>(this));
    }
 
    /** Add metadata to this widget. */
    WidgetArgsType& AddMetaData(TSharedRef<ISlateMetaData> InMetaData)
    {
        MetaData.Add(InMetaData);
        return Me();
    }
 
    /** Add metadata to this widget - convenience method - 1 argument */
    template<typename MetaDataType, typename Arg0Type>
    WidgetArgsType& AddMetaData(Arg0Type InArg0)
    {
        MetaData.Add(MakeShareable(new MetaDataType(InArg0)));
        return Me();
    }
 
    /** Add metadata to this widget - convenience method - 2 arguments */
    template<typename MetaDataType, typename Arg0Type, typename Arg1Type>
    WidgetArgsType& AddMetaData(Arg0Type InArg0, Arg1Type InArg1)
    {
        MetaData.Add(MakeShareable(new MetaDataType(InArg0, InArg1)));
        return Me();
    }
 
//이 아래 부분을 보면 됨..
    SLATE_ATTRIBUTE( FText, ToolTipText )
    SLATE_ARGUMENT( TSharedPtr<IToolTip>, ToolTip )
    SLATE_ATTRIBUTE( TOptional<EMouseCursor::Type>, Cursor )
    SLATE_ATTRIBUTE( bool, IsEnabled )
    SLATE_ATTRIBUTE( EVisibility, Visibility )
    SLATE_ATTRIBUTE( TOptional<FSlateRenderTransform>, RenderTransform )
    SLATE_ATTRIBUTE( FVector2D, RenderTransformPivot )
    SLATE_ARGUMENT( FName, Tag )
    SLATE_ARGUMENT( bool, ForceVolatile )
 
    TArray<TSharedRef<ISlateMetaData>> MetaData;
};
cs

 






여기에서 보면 SLATE_ATTRIBUTE 를 뭉태이로 쓴 것이 보이는데 변수 명 들을 보면 델리게이트와 역일 법한 것들로 보이죠?

즉 SLATE_ATTRIBUTE 로 변수를 만든 다는 것은 나중에 이 변수를 통해서 함수를 바인딩 하고 원하는 시점에 바인딩 된 함수들을 호출하기 위해

존재하는 것 정도로 그 의도를 추측 할 수가 있습니다


이것들을 묶고 있는 클래스 명 (TSlateBaseNamedArgs) 또한 이와 유사한 성향의 연관성을 띈 네이밍으로 볼 수 있습니다













3dmpengines.tistory.com

참고 : 언리얼엔진4 소스
URL : https://www.unrealengine.com


 


반응형

+ Recent posts