tuple에 대한 type traits 구현 몇 가지

2022. 5. 15. 14:27WIP

is_tuple, is_tuple_elemnt

is_tuple은 파라미터 인수로 주어진 타입이 튜플이면 true_type (is_tuple_v도 제공된다)

is_tuple_element는 첫 번째 파라미터 인수가 tuple type(std::tuple)이고. 그 튜플로 부터 추론된 튜플의 파라미터 인자들에 두 번째 파라미터 인수로 주어진 타입이 포함되면 true_type을, 그렇지 않으면 false_type 이다.

 

구현은 다음과 같이 해보았다.

    namespace traits_impl
    {
        template <typename T>
        struct is_tuple_impl : std::false_type { };

        template <typename... T>
        struct is_tuple_impl<std::tuple<T...>> : std::true_type { };

        template <typename TupleT, typename ElementT>
        struct is_tuple_element_impl : std::false_type { };

        template <typename ElementT,  typename... ElementTypes>
        struct is_tuple_element_impl<std::tuple<ElementTypes...>, ElementT> :
            std::conditional_t<
                std::disjunction_v<std::is_same<ElementT, ElementTypes>...>,
                std::true_type,
                std::false_type>
        {
        };
    }

    template <typename T>
    using is_tuple = traits_impl::is_tuple_impl<std::decay_t<T>>;

    template <typename T>
    constexpr bool is_tuple_v = is_tuple<T>::value;

    template <typename TupleT, typename ElementT>
    using is_tuple_element = traits_impl::is_tuple_element_impl<std::decay_t<TupleT>, std::decay_t<ElementT>>;

    template <typename TupleT, typename ElementT>
    constexpr bool is_tuple_element_v = is_tuple_element<TupleT, ElementT>::value;

 튜플인 경우에 특수화 시켜서, 튜플이 아닌 경우엔 false type, 맞으면 true type일 최소한의 조건을 갖추는 식으로.

사실 뭐 is_tuple의 경우엔 스택오버플로에서 찾았고, 여기서 영감을 받아서 is_tuple_element도 한번 만들어 보았다.

is_tuple_element는 첫 번째 인수가 튜플인 경우에 부분 특수화를 시켜서, 특수화 과정에서 추론된 ElementTypes(튜플안 요소들의 타입들)에 대해 is_same traits 그리고 disjunction traits(logical OR of type traits)를 사용해서 하나라도 ElementT와 일치한다면 true type 그렇지 않다면 false type이 되도록 하였다.

 

이걸 만든 이유는 구현중인 ECS 에서 archetype(왜냐면 내가 한 구현에서는 archetype은 component의 tuple이자 또 다른 하나의 component 이기 때문에)을 좀 잘 다루기 위해서.. 

 

 is_tuple 덕분에, 만약 Component가 archetype (tuple)이라면, 그 안에 어떤 component(이걸 archetype의 subcomponent 라고 부르기로 했다!) 가 있는지 정보를 별도로 저장하도록 하였다 (아니면 타입으로 바로 얻어올 수도 있고). 그래서 런타임에도 고유하게 지정된 CompnentID(사실 size_t)를 사용해서 archetype (최소 한개 이상의 Component를 포함하는) 인지 아닌지, 그리고 archetype이라면 subcomponent는 어떤게 있는지 알 수 있다. (여기서 더 나아가서 subcomponent의 타입도 무엇인지 알 수 있다). => 다만 이 경우, 무조건 archetype 내부에 있는 타입이 component 일 것을 가정하고 작성한거라.. 만약 내부에 POD라도 들어 있다면? 

위에서 남은 문제점은 Archetype Macro에 제약 클래스를 하나 만들어서 POD를 포함하는 tuple의 경우엔 아에 컴파일 되지 않도록 하는 방식이나, Component base를 만들어서 이를 무조건 상속하도록 하는 제약을 걸어주는 식으로 해야 할 것 같다. (그런데 Archetype도 Component이기 때문에 이러면 튜플을 내부에 가지고 있는 struct를 만들어 주는 식으로 해야할지도)

 

 is_tuple_element는 앞서 말했던 archetype에 특정 타입이 있고, 그 타입이 특정한 ComponentPool로 특수화 될 수 있도록 하기 위해서 만들었다. (예를들어 component가 archetype이고, archetype 타입 내부에 hierarchy component가 있다면, hierarchy component의 고유한 특성이 고려된 ComponentPool 형태로 archetype의 ComponentPool을 특수화 시킬 수 있도록) 이 부분은 아직 구현은 하진 않았는데 아마 가능하지 않을까 싶다 (tag 를 사용하면).

 

아래 소스코드는 is_tuple과 is_tuple_element의 archetype 버전

template <typename T>
using is_archetype = utils::is_tuple<T>;

template <typename T>
constexpr bool is_archetype_v = is_archetype<T>::value;

template<typename Archetype, typename Component>
using is_archetype_contains = utils::is_tuple_element<Archetype, Component>;

template<typename Archetype, typename Component>
constexpr bool is_archetype_contains_v = is_archetype_contains<Archetype, Component>::value;

아래는 테스트 코드

SceneArchetype archetypeObject{
	HierarchyComponent{GenerateEntity()},
	Renderable()
};

auto& queriedData = QueryArchetypeData<HierarchyComponent>(archetypeObject);
queriedData.parentEntity = GenerateEntity();

auto archetypesSubcomponents = AcquireSubComponentsFromArchetype(archetypeObject);

assert(is_archetype_v<SceneArchetype>);
assert(!is_archetype_v<int>);
assert((is_archetype_contains_v<SceneArchetype, HierarchyComponent>));
assert(!(is_archetype_contains_v<SceneArchetype, int>));