반응형

 

먼저 TPointerIsConvertibleFromTo 이 부분에 대해 알아보자

 

template <typename From, typename To>

struct TPointerIsConvertibleFromTo

{

private:

    static uint8  Test(...);

    static uint16 Test(To*);

 

public:
    //sizeof(Test((From*)nullptr)  해당 함수에 대한 리턴 타입 크기를 얻는다 즉 2 또는 1이 리턴 됨
    //함수 실행 없이 리턴 타입 크기를 얻어올 수 있다

    enum { Value  = sizeof(Test((From*)nullptr)) - 1 };

};

먼저 이건 From 타입에서 To 라는 타입으로 변환 가능하면 Value 값이 1 이 되며 만약 불가능한

포인터 간의 변환이라면 컴파일 타임에 enum 의 Value 값이 0 이 된다

 

static uint8  Test(...); 은     static uint16 Test(To*); 이 아닌 인자를 받기 위한 함수의 가변 형이다

즉 To 타입이 아닌 타입은 리턴 타입 크기를 1로 처리하겠다는 의미

 

ex)..
int printz(...); // valid, but the arguments cannot be accessed portably
보통 가변인자는 앞에 가변인자 개수를 알려주는 정수형이 오지만 위의 경우에는 단지 리턴 타입 사이즈를 얻기위함과 To 가 아닌 타입을 받기위한 처리로 가변인자로 처리한것

 

 

실행 없이 함수의 리턴 타입 크기를 얻어온다( 그래서 int fun(char* a); 까지만 있어도 됨

It returns the size of the return type from that function (4 on my implementation since that's what an int takes up for me), which you would discover had you run it as is, then changed the return type to char (at which point it would give you 1).

The relevant part of the C99 standard is 6.5.3.4.The sizeof operator:

The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.

Keep in mind that bold bit, it means that the function itself is not called (hence the printf within it is not executed). In other words, the output is simply the size of your int type (followed by a newline, of course).

 

 

 

그다음 볼 구문은 TEnableIf

template <bool Predicate, typename Result = void>
class TEnableIf;

 

기본 적으로 봐야 하는 부분은 Predicate 부분인데 Predicate 로 넘어온 값이
1 즉 참이면 Type 이 정의 되고 그렇지 않다면 즉 0이 넘어온다면 Type은 정의 되지 않는다
Result=void 라는 것을 잊지 않고 이어서 나가보자

 

이 구문은 템플릿의 부분 특수화가 적용 되어 있는 구문이다

 

원래 아래와 같은 이러한 템플릿이 존재 했는데 

 

template <bool Predicate, typename Result = void>
class TEnableIf

{

}

 

 

여기서 Predicate 부분에 대해서만 부분적으로 특수화를 진행한다 

부분 특수화를 진행하려면 나머지 인자를 그대로 써주면 된다 그리하여

bool 타입은 true, 또는 false 임으로 이에 대해 부분 특수화를 진행하면 다음처럼 된다

 

template <typename Result>
class TEnableIf<true, Result>
{
public:

   typedef Result Type;   

   //이때는 Type 을 Result 로 정의 하겠다는 의미인데 별다른 타입 정의가 없다면 Result 는
     위의 기본 디폴 트 값으로 인하여 void 타입이 된다

};

template <typename Result>
class TEnableIf<false, Result>
{  

  //즉 이때는 Type 을 정의하지 않겠다, 존재 Type  존재자체가 없어서 Type 을 쓰면

    컴파일 에러를 발생시키겠다는 의미이다

};

 

 

이때

template <bool Predicate, typename Result = void> 
class TEnableIf

{

이 구문을 간략하게 {} 를 제거 하고

 

template <bool Predicate, typename Result = void> 
class TEnableIf;

 

이렇게 표현 하는 것이 가능하다, ture ,false 모두 다 정의했음으로

 

 

 

아래는 이 두개를 응용한 코드이다

#include <type_traits>

#include <iostream>

 

using namespace std;

 

 

template <bool Predicate, typename Result = void>

class TEnableIf;

 

template <typename Result>

class TEnableIf<true, Result>

{

public:

    typedef Result Type;

};

 

template <typename Result>

class TEnableIf<false, Result>

{ };

 

template<typename T>

class aaa {

public:

 

    //enum { Value = sizeof(unsigned short) - 1 };

    enum { Value = sizeof(unsigned short- 1 };

};

 

template<typename = TEnableIf<aaa<int>::Value>::Type>

void fn()

{

 

}

 

int main()

{

    fn();

    aaa<int> ai;

    auto ddddd= ai.Value;

 

    auto sdfsd=aaa<int>::Value;

 

    TEnableIf<aaa<int>::Value> sdf;

    

    //TEnableIf<aaa<int>::Value>::Type* sdfdd;

    //TEnableIf<true>::Type sdfd;

 

 

    return 0;

}

 

 

 

 

TSubclassOf (1)

다른 타입 변환 간의 안정적인 타입을 확인하고자 하는 템플릿 클래스

이 클래스는 UClass* 를 멤버 변수로 담는 템프릿 클래스이다

 

template<class TClass>
class TSubclassOf
{

....

private:
UClass* Class;

}

 

상세 구문..

template<class TClass>
class TSubclassOf
{
    template <class TClassA>
    friend class TSubclassOf;
public:
    /** Default Constructor, defaults to null */
    FORCEINLINE TSubclassOf() :
        Class(nullptr)
    {
    }
    /** Constructor that takes a UClass and does a runtime check to make sure this is a compatible class */
    FORCEINLINE TSubclassOf(UClass* From) :
        Class(From)
    {
    }
    /** Copy Constructor, will only compile if types are compatible */
    template <class TClassA, class = typename TEnableIf<TPointerIsConvertibleFromTo<TClassA, TClass>::Value>::Type>
    FORCEINLINE TSubclassOf(const TSubclassOf<TClassA>& From) :
        Class(*From)
    {
    }
    /** Assignment operator, will only compile if types are compatible */
    template <class TClassA, class = typename TEnableIf<TPointerIsConvertibleFromTo<TClassA, TClass>::Value>::Type>
    FORCEINLINE TSubclassOf& operator=(const TSubclassOf<TClassA>& From)
    {
        Class = *From;
        return *this;
    }
 
 
...중략...
 
private:
    UClass* Class;
};
 

 

이 중에서도 아래 구문을 보자

 

우선 아래 구문은 TSubclassOf 에 대한 대입 연산자 구문을 기반으로 진행된다

 

TClassA 이것은 자기 자신이 아닌 assign 될 대상 타입이 된다
TClass 가 자기 자신 타입, 즉 넘어온 타입이 자기 자신으로 포인터 변환이 가능한지 여부에 따라 컴파일 에러를 벹게 할것인지 아닌지를 결정한다

 

/** Copy Constructor, will only compile if types are compatible */

    template<class TClassA

    class = typename TEnableIf<TPointerIsConvertibleFromTo<TClassATClass>::Value>::Type>

    FORCEINLINE TSubclassOf& operator=(const TSubclassOf<TClassA>& From)

    {

        Class = *From;

        return *this;

    }

 

TEnableIf 과 TPointerIsConvertibleFromTo 의 조합임을 알 수 있다 

TPointerIsConvertibleFromTo 의 결과 값인 Value 값이 1 이면 => 변환 가능한 포인터라면

TEnableIf 에서 Type 을 typede 하게 되고 Type 은 void 처리 되어 class=void 가 되어

컴파일 에러가 발생하지 않지만 그렇지 않은 경우에는 컴파일 오류를 발생시킨다

class=   이렇게만 됨으로

 

Copy Constructor, 는 형식이 호환 되는 경우에만 컴파일 처리한다

 

 

//컴파일 가능

template<typename = void >
void fn3() {
}

fn3();

 ....

 

 

//컴파일 불가

template<typename =  >
void fn3() {
}

fn3();

 

 

 

TSubclassOf (2)

TSubclassOf 는 UClass 유형의 안전성을 보장해 주는 템플릿 클래스입니다. 예를 들어 디자이너가 대미지 유형을 지정하도록 해주는 프로젝타일 클래스를 제작중이라 가정합시다. 그냥 UPROPERTY 유형의 UClass 를 만든 다음 디자이너가 항상 UDamageType 파생 클래스만 할당하기를 바라거나, TSubclassOf 템플릿을 사용하여 선택지를 제한시킬 수도 있습니다. 그 차이점은 아래 코드와 같습니다:

 

두 번째 선언에서, 템플릿 클래스는 에디터의 프로퍼티 창에 UDamageType 파생 클래스만 선택되도록 합니다. 첫 번째 선언에서는 아무 UClass 나 선택할 수 있습니다. 아래 그림에서 확인됩니다.

 

 

이러한 UPROPERTY 안전성에 추가로, C++ 수준에서의 유형 안전성도 확보할 수 있습니다.

 

비호환 TSubclassOf 유형을 서로에게 할당하려는 순간, 컴파일 오류가 나게 됩니다.

(위에 언급한 이유로)

 

범용 UClass 를 할당하려는 경우, 할당이 가능한지 검증하는 실행시간 검사를 합니다.

실행시간 검사가 실패하면, 결과값은 nullptr 입니다.

 

UClass* ClassA = UDamageType::StaticClass();
 
TSubclassOf<UDamageType> ClassB;
 
ClassB = ClassA; // Performs a runtime check //런타임때 체크
 
TSubclassOf<UDamageType_Lava> ClassC;

//컴파일 타임때 체크
ClassB = ClassC; // Performs a compile time check

 

ref :  

https://stackoverflow.com/questions/12659486/what-does-sizeof-functionargument-return

https://docs.unrealengine.com/en-US/API/Runtime/Core/Templates/TPointerIsConvertibleFromTo/index.html

 

https://docs.unrealengine.com/ko/Programming/UnrealArchitecture/TSubclassOf/index.html

https://en.cppreference.com/w/cpp/language/variadic_arguments

반응형

+ Recent posts