Typelist Class Generation

원래 C++ Modern Design 책에서는 GenScatterHierarchy 라는 이름으로 되어있지만 너무 길어서 이름을 좀 줄여보았다.
또한 내 필요에 의해 약간 수정사항도 있다.

TUxGenClass는 typelist에 있는 type 모두의 인스턴스를 클래스 안에 생성하도록 해준다.
만약 int 타입이 3개 들어가있는 typelist가 있다면 TUxGenClass를 통해 int [3] 가 있는 클래스로 변신하는 것이다.

namespace UGF { namespace MPL {
	///////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Typelist에 있는 타입들의 클래스를 생성하는 클래스
	template <typename TPxTypeList> class TUxGenClass {};
	template <typename TPxHead, typename TPxTail>
	class TUxGenClass<TUxTypeList<TPxHead,TPxTail>> : public TUxGenClass<TPxTail>
	{
	public:
		typedef TUxTypeList<TPxHead,TPxTail>	TTxTypeList;
		typedef TPxHead							TTxValueType;
		typedef TUxGenClass<TPxTail>			TTxBaseType;
	public:
		template <typename TPxParamType, template <typename,typename> class TPxFuncPolicy>
		uxbool CallByIndex( ux32s nFieldIndex, TPxParamType& param )
		{
			if ( nFieldIndex < 0 )
				return false;
			if ( nFieldIndex == 0 )
				return TPxFuncPolicy<TTxValueType,TPxParamType>::Do( Value, param );
			return TTxBaseType::CallByIndex<TPxParamType,TPxFuncPolicy>( nFieldIndex - 1, param );
		}

	public:
		TTxValueType	Value;
	};
	template <typename TPxType>
	class TUxGenClass<TUxTypeList<TPxType,TTxNullType>>
	{
	public:
		typedef TUxTypeList<TPxType,TTxNullType>	TTxTypeList;
		typedef TPxType								TTxValueType;
	public:
		template <typename TPxParamType, template <typename,typename> class TPxFuncPolicy>
		uxbool CallByIndex( ux32s nFieldIndex, TPxParamType& param )
		{
			if ( nFieldIndex < 0 )
				return false;
			if ( nFieldIndex == 0 )
				return TPxFuncPolicy<TTxValueType,TPxParamType>::Do( Value, param );
			return false;
		}
	public:
		TTxValueType Value;
	};
} }

클래스는 이따위로 생겼다.
책에서 추가된 부분은 CallByIndex함수인데, 이 함수로 인해 컴파일 시점과 런타임 시점의 경계를 넘나들 수 있게 된다.
이 클래스는 field의 인덱스를 템플릿 파라메터로 주도록 되어있다. 따라서 런타임 인덱스 변화에 따라 처리하기 곤란하다.
하지만 CallByIndex를 사용하면 런타임 값에 의해 해당 field에 대한 처리가 가능하다.

이렇게 하기 위해서 어쩔 수 없이 좀 지저분한 코드가 들어가게 됐는데, CallByIndex 파라메터의 TPxFuncPolicy가 그것이다.
TPxFuncPolicy는 두 개의 템플릿 파라메터를 받는다.

1: 특정 인덱스의 값 타입,
2: CallByIndex caller가 넘겨주는 파라메터

그리고 TPxFuncPolicy로 넘어가는 클래스 타입은 uxbool값을 반환하는 Do( ValueType&, ParamType& )의 static 함수가 있어야 하고, 그 안에서 하고싶은 처리를 해야 한다.

위의 함수로는 특정 인덱스의 field를 얻는데 많은 불편함이 있기 때문에 아래와 같은 함수도 책에서 베껴왔다.

namespace UGF { namespace MPL {	namespace GenClass {
	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// TUxGenClass의 필드를 access하는 기능
	template <typename TPxGenClass, ux32s NPxIndex> class TUxFieldHelper;
	template <typename TPxGenClass>
	class TUxFieldHelper<TPxGenClass,0>
	{
	public:
		typedef typename TPxGenClass::TTxValueType	TTxResultType;
	public:
		static TTxResultType& Get( TPxGenClass& obj )
		{
			return obj.Value;
		}
	};
	template <typename TPxGenClass, ux32s NPxIndex>
	class TUxFieldHelper<TPxGenClass,NPxIndex>
	{
	public:
		typedef typename UGF::MPL::Typelist::TFxTypeAt<typename TPxGenClass::TTxTypeList,NPxIndex>::TTxResult	TTxResultType;
	public:
		static TTxResultType& Get( TPxGenClass& obj )
		{
			typename TPxGenClass::TTxBaseType& Base = obj;
			return TUxFieldHelper<typename TPxGenClass::TTxBaseType,NPxIndex - 1>::Get( obj );
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// 좀더 쉽게 사용하려고 TUxFieldHelper를 사용하는 글로벌 함수 선언
	template <ux32s NPxIndex, typename TPxGenClass>
	typename TUxFieldHelper<TPxGenClass,NPxIndex>::TTxResultType& TFxField( TPxGenClass& obj )
	{
		return TUxFieldHelper<TPxGenClass,NPxIndex>::Get( obj );
	}
} } }

위 기능을 가지고 아래와 같은 코드를 짤 수 있다. 아래 코드는 ux32f type의 3차원 벡터 타입을 선언하는 예제이다.

typedef TUxGenClass<MUxTypelist3(ux32f,ux32f,ux32f)> TTxVector3f;

TTxVector3f vec;
GenClass::TFxField<0>( vec ) = 0.0f;  // x component
GenClass::TFxField<1>( vec ) = 0.0f;  // y component
GenClass::TFxField<2>( vec ) = 1.0f;  // z component

UGF의 MPL 기본적인 기능.

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// 컴파일 assertion 매크로
#define STATIC_ASSERT_DECL(n,b)											\
	template <uxbool BPxCondition> class __check_assert_class##n;		\
	template <> class __check_assert_class##n<true> {};					\
	 __check_assert_class##n<(b)!=0> __check_assert_inst_##n;
#define STATIC_ASSERT(n,b)	ux8s __check_assert_var##n[(b)?1:0]; __check_assert_var##n;

namespace UGF { namespace MPL {
	//////////////////////////////////////////////////////////////////////////////////////////////////////////
	// BPxCondition에 따라 TPxTrueType 혹은 TPxFalseType을 결과로 내놓는다.
	template <uxbool BPxCondition, typename TPxTrueType, typename TPxFalseType>
	class TFxSelect;
	template <typename TPxTrueType, typename TPxFalseType>
	class TFxSelect<true,TPxTrueType,TPxFalseType>
	{
	public:
		typedef TPxTrueType		TTxResult;
	};
	template <typename TPxTrueType, typename TPxFalseType>
	class TFxSelect<false,TPxTrueType,TPxFalseType>
	{
	public:
		typedef TPxFalseType	TTxResult;
	};

	//////////////////////////////////////////////////////////////////////////////////////////////////////////
	// 정수 값을 하나의 타입으로 만든다.
	template <ux8s NPxValue> class TFxInt8sType {public: enum { NUxResult = NPxValue };};
	template <ux8u NPxValue> class TFxInt8uType {public: enum { NUxResult = NPxValue };};
	template <ux16s NPxValue> class TFxInt16sType {public: enum { NUxResult = NPxValue };};
	template <ux16u NPxValue> class TFxInt16uType {public: enum { NUxResult = NPxValue };};
	template <ux32s NPxValue> class TFxInt32sType {public: enum { NUxResult = NPxValue };};
	template <ux32u NPxValue> class TFxInt32uType {public: enum { NUxResult = NPxValue };};
	template <ux64s NPxValue> class TFxInt64sType {public: enum { NUxResult = NPxValue };};
	template <ux64u NPxValue> class TFxInt64uType {public: enum { NUxResult = NPxValue };};

	//////////////////////////////////////////////////////////////////////////////////////////////////////////
	// 두 타입이 정확하게 같은 타입인지 검사한다.
	template <typename TPxTypeL, typename TPxTypeR>
	class TFxIsSameType
	{
	public:
		enum { BUxResult = 0 };
	};
	template <typename TPxType>
	class TFxIsSameType<TPxType,TPxType>
	{
	public:
		enum { BUxResult = 1 };
	};

	//////////////////////////////////////////////////////////////////////////////////////////////////////////
	// TPxTypeL이 TPxTypeR로 변환 가능한지 검사한다.
	template <typename TPxTypeL, typename TPxTypeR>
	class TFxIsConvertibleL2R
	{
	private:
		struct SUxTrueType	{ ux8s __dummy; };
		struct SUxFalseType	{ SUxTrueType __dummy[2]; };

	private:
		static const TPxTypeL& FromType( void );
		static SUxTrueType TestFunc( TPxTypeR );
		static SUxFalseType TestFunc( ... );

	public:
		enum { BUxResult = sizeof( TestFunc( FromType() ) ) == sizeof( SUxTrueType ), };
	};

	//////////////////////////////////////////////////////////////////////////////////////////////////////////
	// TPxTypeR이 TPxTypeL로 변환 가능한지 검사한다.
	template <typename TPxTypeL, typename TPxTypeR>
	class TFxIsConvertibleR2L : public TFxIsConvertibleL2R<TPxTypeR,TPxTypeL> {};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////
	// 아무 기능도 없는 null type
	class TTxNullType {};
} }

STATIC_ASSERT_DECL / STATIC_ASSERT는 컴파일 타임에 오류를 내는 매크로이며, 조건문이 실패했을 때 컴파일 실패하는 구문을 선언하도록 하여 고의적으로 컴파일 실패하게 하도록 한다.
이 assert문은 선언을 해야 하기 때문에 똑같은 이름으로 사용할 수 없어 어쩔 수 없이 인덱스를 받는다. 같은 scope내에서 여러개의 assert를 사용할 경우 서로 다른 인덱스를 부여해주어야 한다.
또한 클래스 내부에 선언하는 방식과 함수 내부에 선언하는 방식 두 가지가 서로 다르기 때문에 매크로를 두 개로 만들었다. STATIC_ASSERT_DECL은 클래스에 사용하는 매크로이고, STATIC_ASSERT는 함수 내부에서 사용되어야 한다. STATIC_ASSERT에서 변수 선언만 해놓고 사용하지 않으면 경고가 지저분하게 뜨기 때문에 약간의 처리를 해주었다.

MPL-based State Machine (Specifying & Implementing…)

  1. Preface

    UGF의 모든 시스템은 state machine 형태로 개발될 것이다. {아주}* 짧은 게임 개발 경력이지만 게임을 개발하면서 불편하고, 가장 버그가 발생할 확률이 높은 부분이 오브젝트에 대한 상태 관리인 것 같았다.

    좀만 생각해 보면 현재 거의 대세로 굳혀진 객체 지향 프로그래밍(OOP, Object Oriented Programming)에서도 객체의 상태에 따라 객체가 다음에 어떤 행동을 할지 결정을 한다. 이 과정에서 if-else 혹은 switch 문이 나열되어 보기도 어렵고, 각 상태에 대한 어떤 constraint가 명시적이지 않기 때문에, 나같이 우매한 코더들이 버그를 만들기 딱 좋다.

    또한 UE3(Unreal Engine 3)를 볼 기회가 있어 Unreal Script를 봤는데, 언어 자체에 state machine을 지원하고 있었다. Unreal Script 자체를 사용하는 것은 짜증났지만 state machine을 언어 차원에서 지원한다는 것은 매우 부러웠다. 왜냐.. C++에는 없기 때문에..

    그렇다고 해서 나 같은 나부랭이가 C++ 표준 위원회에 "state machine 좀 넣어 주쇼" 이럴 수도 없는 노릇이다. 그래서 눈을 돌린 것이 "meta programming"이다. 어떤 분은 "meta programming"이 흑마법이라고 얘기를 하지만, 흑마법이건 백마법이건 내 목적만 이루면 된다라는 생각에 무모하게 도전해본다.

    일단 MPL(Meta Programming Library)-based State Machine의 목표는 아래와 같다.

      1. 가능한 simple하게. Template을 아는 사람이라면 쉽게 가져다 쓸 수 있도록
      2. Fundamental library임을 감안하여 state machine이라는 범주 안에서 원하는 기능은 모두  구현 가능하도록 확장성은 최대
      3. State와 transition에 대한 constraint는 강력하게 검사.
      4. 잘못된 사용(state machine에 없는 state를 사용하려 했다거나)하려 하면 컴파일 타임 에러

    과연, 성공할 수 있을까?

     현재 약 1달 째 테스트 해보며 필요한 기능에 대한 목록 작성하면서 졸라 빡침.

  2. State Machine

    1. State Class (Spec 수정중..)

      1. Introduction
         
      2. Class Definition
      3. State Event
        1. State Leaving
        2. State Entered
      4. Expanding State Machine
        1. State Extending (State Machine Evolution)
        2. State Overriding (State Machine Mutation)
    2. Transition Class
      1. Introduction
      2. Class Definition
      3. Synchronization
    3. State Manager Class
      1. Ticking State Machine
  3. Other Topics

    1. Debugging Support
      1. Backtracing Transitions
        1. Tracing Transition Stack
          1. Transition Time
          2. Tick Number
          3. Transition List
          4. Stack List
      2. Catching State Fault
      3. Detecting Transition Dead-Lock
  4. Implementation Guide

    1. Finite State Machine
    2. Fuzzy State Machine

Typelist

처음 C를 공부할 때 linked-list를 구현해보듯이 메타 프로그래밍의 시작점은 typelist라 생각 된다.

Typelist는 컴파일 타임에 여러 type들을 list로 관리하며 그 type들을 가지고 여러가지 작업(?)을 할 때 사용된다.
Typelist의 선언은 아래 코드와 같고, typelist를 쉽게 사용하기 위해서 typelist 선언 매크로를 넣어보았다.

namespace UGF { namespace MPL { namespace Typelist {
	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// TPxTypeList의 길이를 얻어온다.
	template <typename TPxTypeList> class TFxLength;
	template <>
	class TFxLength<TTxNullType>
	{
	public:
		enum { NUxResult = 0 };
	};
	template <typename TPxHead, typename TPxTail>
	class TFxLength<TUxTypeList<TPxHead,TPxTail>>
	{
	public:
		enum { NUxResult = 1 + TFxLength<TPxTail>::NUxResult };
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// TPxTypeList에서 NPxIndex번째 있는 type을 얻어온다.
	// NPxIndex가 범위를 벗어나면 컴파일 오류난다.
	template <typename TPxTypeList, ux32u NPxIndex> class TFxTypeAt;
	template <typename TPxHead, typename TPxTail>
	class TFxTypeAt<TUxTypeList<TPxHead,TPxTail>,0>
	{
	public:
		typedef TPxHead TTxResult;
	};
	template <typename TPxHead, typename TPxTail, ux32u NPxIndex>
	class TFxTypeAt<TUxTypeList<TPxHead,TPxTail>,NPxIndex>
	{
	public:
		typedef typename TFxTypeAt<TPxTail,NPxIndex-1>::TTxResult TTxResult;
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// TPxTypeList에서 TPxType과 TPxCompare조건에 부합하는 type의 가장 작은 인덱스를 얻어온다.
	// TPxCompare는 2항을 받는 template class이어야 한다.
	template <typename TPxTypeList, typename TPxType, template <typename,typename> class TPxCompare>
	class TFxFindIndex;
	template <typename TPxType, template <typename,typename> class TPxCompare>
	class TFxFindIndex<TTxNullType,TPxType,TPxCompare>
	{
	public:
		enum { NUxResult = -1, };
	};
	template <typename TPxHead, typename TPxTail, typename TPxType, template <typename,typename> class TPxCompare>
	class TFxFindIndex<TUxTypeList<TPxHead,TPxTail>,TPxType,TPxCompare>
	{
	private:
		enum
		{
			IsMatch			= TPxCompare<TPxHead,TPxType>::BUxResult,
			IndexInTail		= TFxFindIndex<TPxTail,TPxType,TPxCompare>::NUxResult,
			ResultInTail	= IndexInTail == -1 ? -1 : IndexInTail + 1,
		};
	public:
		enum { NUxResult = IsMatch ? 0 : ( IndexInTail == -1 ? -1 : IndexInTail + 1 ), };
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// TPxTypeList에서 TPxType과 완전 똑같은 타입의 인덱스를 얻어온다.
	template <typename TPxTypeList, typename TPxType>
	class TFxIndexOf : public TFxFindIndex<TPxTypeList,TPxType,TFxIsSameType> { };

	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// Typelist안에 TPxCompare 조건에 부합하는 type이 몇 개 있는지 얻어온다.
	// TPxCompare는 2항을 받는 template class이어야 한다.
	template <typename TPxTypeList, typename TPxType, template <typename,typename> class TPxCompare>
	class TFxCount;
	template <typename TPxType, template <typename,typename> class TPxCompare>
	class TFxCount<TTxNullType,TPxType,TPxCompare>
	{
	public:
		enum { NUxResult = 0, };
	};
	template <typename TPxHead, typename TPxTail, typename TPxType, template <typename,typename> class TPxCompare>
	class TFxCount<TUxTypeList<TPxHead,TPxTail>,TPxType,TPxCompare>
	{
	private:
		enum
		{
			IsMatch			= TPxCompare<TPxHead,TPxType>::BUxResult,
			ResultInTail	= TFxCount<TPxTail,TPxType,TPxCompare>::NUxResult,
		};

	public:
		enum { NUxResult = ResultInTail + ( IsMatch ? 1 : 0 ), };
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// Typelist 안에 같은 타입이 있는지 검사한다.
	template <typename TPxTList> class TFxCheckRedundancy;
	template <typename TPxHead, typename TPxTail>
	class TFxCheckRedundancy<TUxTypeList<TPxHead,TPxTail>>
	{
	private:
		enum
		{
			CheckCurrType = TFxIndexOf<TPxTail,TPxHead>::NUxResult != -1 ? 1 : 0,
			CheckSubList = TFxCheckRedundancy<TPxTail>::BUxResult,
		};
	public:
		enum { BUxResult = (CheckCurrType + CheckSubList) != 0 };
	};
	template <>
	class TFxCheckRedundancy<TTxNullType>
	{
	public:
		enum { BUxResult = 0 };
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////
	// TPxTypeListL 끝에 TPxTypeListR을 붙인 결과를 얻는다.
	template <typename TPxTypeListL, typename TPxTypeListR> class TFxAppend;
	template <>
	class TFxAppend<TTxNullType,TTxNullType>
	{
	public:
		typedef TTxNullType						TTxResult;
	};
	template <typename TPxType>
	class TFxAppend<TTxNullType,TPxType>
	{
	public:
		typedef MUxTypelist1(TPxType)			TTxResult;
	};
	template <typename TPxHead, typename TPxTail>
	class TFxAppend<TTxNullType,TUxTypeList<TPxHead,TPxTail>>
	{
	public:
		typedef TUxTypeList<TPxHead,TPxTail>	TTxResult;
	};
	template <typename TPxHead, typename TPxTail, typename TPxType>
	class TFxAppend<TUxTypeList<TPxHead,TPxTail>,TPxType>
	{
	public:
		typedef TUxTypeList<TPxHead,typename TFxAppend<TPxTail,TPxType>::TTxResult>	TTxResult;
	};

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Typelist에서 NPxIndex번째 type을 제거한 typelist를 얻는다.
	// NPxIndex가 범위를 벗어나면 컴파일 오류가 발생한다.
	template <typename TPxTypeList, ux32u NPxIndex> class TFxRemoveAt;
	template <typename TPxHead, typename TPxTail>
	class TFxRemoveAt<TUxTypeList<TPxHead,TPxTail>,0>
	{
	public:
		typedef TPxTail								TTxResult;
	};
	template <ux32u NPxIndex>
	class TFxRemoveAt<TTxNullType,NPxIndex>
	{
	public:
		typedef TTxNullType							TTxResult;
	};
	template <typename TPxHead, typename TPxTail, ux32u NPxIndex>
	class TFxRemoveAt<TUxTypeList<TPxHead,TPxTail>,NPxIndex>
	{
	private:
		typedef typename TFxRemoveAt<TPxTail,NPxIndex-1>::TTxResult	TTxTailResult;

	public:
		typedef TUxTypeList<TPxHead,TTxTailResult>	TTxResult;
	};

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// TPxType과 똑같은 type 중 index가 가장 작은 하나를 없앤 결과 typelist를 얻는다.
	template <typename TPxTypeList, typename TPxType> class TFxRemoveOf;
	template <typename TPxType>
	class TFxRemoveOf<TTxNullType,TPxType>
	{
	public:
		typedef TTxNullType		TTxResult;
	};
	template <typename TPxHead, typename TPxTail>
	class TFxRemoveOf<TUxTypeList<TPxHead,TPxTail>,TPxHead>
	{
	public:
		typename TPxTail		TTxResult;
	};
	template <typename TPxHead, typename TPxTail, typename TPxType>
	class TFxRemoveOf<TUxTypeList<TPxHead,TPxTail>,TPxType>
	{
	private:
		typedef typename TFxRemoveOf<TPxTail,TPxType>::TTxResult	TTxTailResult;
	public:
		typedef TTxTailResult	TTxResult;
	};
} } }

위 기능들을 음미하면서 구현하게 되면 메타 프로그래밍에 대한 감각을 익힐 수 있으며, 특히 메타 프로그래밍에서 loop가 어떻게 동작하는지 공부할 수 있다.

Naming Convention

2010/12/15 2개의 댓글

기본 타입들

<PrimitiveType> := ux<TYPE>
<TYPE> := bool | size | <INTEGER_TYPE> | <REAL_TYPE> | <STRING_TYPE>
<INTEGER_TYPE> := <INTEGER_DATA_SIZE> | <INTEGER_DATA_TYPE>
<INTEGER_DATA_SIZE> := 8 | 16 | 32 | 64 | ptr
<INTEGER_DATA_TYPE> := s | u
<REAL_TYPE> := <REAL_DATA_SIZE>f
<REAL_DATA_SIZE> := 32 | 64
<STRING_TYPE> := <ENCODE_RESTRICTED_TYPE> | <ENCODE_FREE_TYPE>
<ENCODE_RESTRICTED_TYPE> := <CHAR_BIT_COUNT> | <CHAR_TYPE>
<CHAR_BIT_COUNT> := 8 | 16
<CHAR_TYPE> := c | str | cstr
<ENCODE_FREE_TYPE> := <CHAR_TYPE>

위의 규칙에 따라 아래와 같은 타입들이 정의되어 있다.

// Boolean type definitions
typedef bool uxbool;

// Numeric type definitions
typedef char ux8s;
typedef unsigned char ux8u;
typedef short ux16s;
typedef unsigned short ux16u;
typedef long ux32s;
typedef unsigned long ux32u;
typedef __int64 ux64s;
typedef unsigned __int64 ux64u;
typedef float ux32f;
typedef double ux64f;

// Size type definitions
typedef size_t uxsize;

// Byte-restricted character & string type definitions
typedef char ux8c;
typedef unsigned short ux16c;
typedef char * ux8str;
typedef const char * ux8cstr;
typedef wchar_t * ux16str;
typedef const wchar_t *ux16cstr;

// Pointer type definitions
typedef INT_PTR uxptrs;
typedef UINT_PTR uxptru;

// Byte-freed character & string type definitions
#ifdef _UNICODE
typedef wchar_t uxc;
typedef wchar_t* uxstr;
typedef const wchar_t* uxcstr;
#else
typedef char uxc;
typedef char* uxstr;
typedef const char* uxcstr;
#endif

사용자 정의 타입들

<USER_DEFINED_TYPE> := <TYPE_CLASSIFIER><USAGE>x<CLASS_NAME>
<DECLARE_CLASSIFICATION> := C | I | T | N | M | E | S

  • C : 완전 구현된 클래스로 선언
  • I : 인터페이스로 선언
  • T : 템플릿으로 선언 또는 컴파일 타임에 결정되는 type 선언
  • N : 숫자형으로 선언 (주로 템플릿 파라메터에서 사용됨)
  • B : Boolean형으로 선언 (주로 템플릿 파라메터에서 사용됨)
  • M : 매크로 선언
  • E : 열거형 선언
  • S : 구조체 선언
  • G : 전역으로 선언

<USAGE> := U | P | T | F

  • U : 일반적으로 거시기하게 사용되는 type
  • P : 파라메터로 사용되는 type
  • T : 재정의된 type
  • F : 함수로 사용되는 type 혹은 함수. 메타 프로그래밍에서는 함수적인 역할의 템플릿 클래스도 해당됨.
카테고리:Foundations 태그: