2014-06-06 5 views
3

C++에서 다음 패턴으로 점진적으로 컴파일 시간 목록을 빌드 할 수 있습니까?C++에서 점진적으로 컴파일 시간 목록 작성

START_LIST(List) 
ADD_TO_LIST(List, int) 
ADD_TO_LIST(List, float) 
ADD_TO_LIST(List, double) 
END_LIST(List) 

이의 결과가 동일해야 :

using List = Cons<int, Cons<float, Cons<double, Nil>>>; 

나는 또한 매크로 사이의 공간이 모든 것은 무엇이든 범위에서해야하는 제한이있다. 나는 이런 식으로 뭔가를 일을 정의하고 매크로를 사용하여 동시에 목록을 등록 할 계획입니다 :

#define DEFINE_ELEMENT(Name, Value) \ 
using Name = SomeTemplate<Value>; \ 
ADD_TO_LIST(ListOfElements, Name) 

즉,이 SomeTemplate< 또는 decltype(처럼 뭔가 START_LIST을 정의 할 수 없습니다. 그러면 새로운 정의를 추가 할 수 없게됩니다.

솔루션은 대안으로 "매개 변수 팩"(가변적 템플릿) 목록의 형태 일 수 있습니다. 위의 그림과 같이 점증 적 정의 패턴을 따르는 것만 신경 쓴다.

여기에 전문화를 사용할 수 있습니까? 위의 패턴을 정확히 처리 할 수 ​​없다면 좀 더 상용구를 사용할 수 있습니까?

+0

나는 컴파일러에 의존 할 것이라고 생각한다. 어떤 컴파일러를 사용하고 있습니까? 나는 나의 마지막 직업에서 그와 비슷한 것을 보았다. 슬프게도, 나는 세부 사항을 기억하지 않는다. – abelenky

+0

@abelenky 표준 C++ 11 솔루션을 기대하고 있습니다.하지만 G ++ 4.8에서 작동하는 것과 함께 할 것이라고 생각합니다. –

+2

내 대답을 좋아할 수도 있습니다 : http://stackoverflow.com/questions/18701798/building-and-accessing-a-list-of-types-at-compile-time/18704609#18704609 –

답변

1

를 OP 자신에 : 당신이 구문을 더 명확하게하기 위해 템플릿 별칭을 사용할 수 물론

//l is list<int,list<char,list<bool,nil>>> 
using l = typename make_list<int,char,bool>::result; 

솔루션을 사용하는 경우 전역 범위, 클래스 범위 또는 함수 범위에서만 작동합니다. 내 구현은 전역, 클래스 및 기능 범위 모두에서 작동합니다. OP 솔루션에 대한 또 다른 장점은 내 솔루션으로 여러 목록 START_LIST/END_LIST 쌍이 겹치도록 허용하는 것입니다. 즉, 서로 다른 목록 구성이 인터리빙 될 수 있습니다.

하나의 작은 제한은 starndard의 일부가 아닌 __COUNTER__ 매크로를 사용하지만 gcc, clang 및 MSVC에서 잘 지원되므로 이식성이 큰 문제는 아닙니다. 또 다른 것은 함수 범위를위한 것입니다. 함수 오버플로 해상도를 사용하기 때문에 START_LIST_FUNCADD_TO_LIST_FUNC이라는 별도의 매크로를 사용해야하지만 클래스 범위에서는 static 함수를 사용해야하지만 함수 범위에서는 static 함수를 선언 할 수 없습니다.

편집 : OP의 주석에서 ListReverseHelper라는 아이디어를 통합하여 훨씬 간단하게 만듭니다. ++ g에서 컴파일

#include <iostream> 
#include <typeinfo> 
using namespace std; 

struct Nil {}; 

template <typename T, typename U> struct Cons {}; 

template <typename List, typename Reversed> struct ListReverseHelper; 

template <typename Reversed> 
struct ListReverseHelper<Nil, Reversed> { 
    using Type = Reversed; 
}; 

template <typename Head, typename Tail, typename Reversed> 
struct ListReverseHelper<Cons<Head, Tail>, Reversed> { 
    using Type = typename ListReverseHelper<Tail, Cons<Head, Reversed>>::Type; 
}; 

template <typename T, int N> struct ListMakerKey : ListMakerKey<T, N-1> {}; 
template <typename T> struct ListMakerKey<T, 0> {}; 

#define START_LIST_(name, modifier) \ 
    struct name##_ListMaker {}; \ 
    modifier Nil list_maker_helper_(ListMakerKey<name##_ListMaker, __COUNTER__>); 
#define ADD_TO_LIST_(name, type, modifier) \ 
    modifier Cons<type, decltype(list_maker_helper_(ListMakerKey<name##_ListMaker, __COUNTER__>{}))> \ 
    list_maker_helper_(ListMakerKey<name##_ListMaker, __COUNTER__>); 
#define END_LIST(name) \ 
    using name = typename ListReverseHelper<decltype(list_maker_helper_(ListMakerKey<name##_ListMaker, __COUNTER__>{})), Nil>::Type; 

#define START_LIST(name) START_LIST_(name, static) 
#define ADD_TO_LIST(name, type) ADD_TO_LIST_(name, type, static) 
#define START_LIST_FUNC(name) START_LIST_(name,) 
#define ADD_TO_LIST_FUNC(name, type) ADD_TO_LIST_(name, type,) 

START_LIST(List) 
ADD_TO_LIST(List, int) 
int a = 10; 
ADD_TO_LIST(List, float) 
int b = 10; 
START_LIST(List2) 
ADD_TO_LIST(List, int) 
int c = 10; 
ADD_TO_LIST(List2, float) 
ADD_TO_LIST(List, double) 
ADD_TO_LIST(List2, int) 
ADD_TO_LIST(List2, float) 
END_LIST(List2) 
ADD_TO_LIST(List, double) 
ADD_TO_LIST(List, char) 
END_LIST(List) 

struct A { 
    START_LIST(List3) 
    ADD_TO_LIST(List3, int) 
    int a = 10; 
    ADD_TO_LIST(List3, float) 
    int b = 10; 
    ADD_TO_LIST(List3, double) 
    ADD_TO_LIST(List3, int) 
    END_LIST(List3) 
}; 

int main() { 
    START_LIST_FUNC(List4) 
    ADD_TO_LIST_FUNC(List4, char) 
    int a = 10; 
    ADD_TO_LIST_FUNC(List4, float) 
    int b = 10; 
    ADD_TO_LIST_FUNC(List4, int) 
    ADD_TO_LIST_FUNC(List4, char) 
    END_LIST(List4) 
    List x; 
    List2 y; 
    A::List3 z; 
    List4 w; 
    cout << typeid(x).name() << endl; 
    cout << typeid(y).name() << endl; 
    cout << typeid(z).name() << endl; 
    cout << typeid(w).name() << endl; 
} 

-4.8은 :

[hidden]$ g++ -std=c++11 x.cpp && c++filt -t `./a.out` 
Cons<int, Cons<float, Cons<int, Cons<double, Cons<double, Cons<char, Nil> > > > > > 
Cons<float, Cons<int, Cons<float, Nil> > > 
Cons<int, Cons<float, Cons<double, Cons<int, Nil> > > > 
Cons<char, Cons<float, Cons<int, Cons<char, Nil> > > > 
+0

이 문제는 ADD_TO_LIST 호출 사이에 임의의 정의를 삽입 할 수 없다는 것입니다. –

+0

그것은 내 것보다 훨씬 simipler입니다. http://coliru.stacked-crooked.com/a/d7f802f3f225f476 –

+0

@AmbrozBizjak : 사이에 임의의 정의를 추가 하시겠습니까? 이것은 큰 문제이며, 질문에서 명확히 밝혀야한다. (또한이를 불가능 영역으로 이동) –

2
당신은, 그들은 고전적인 기능 head:tail 접근 방식보다 훨씬 더 멋진 방법으로 typelists를 작성할 수 있습니다 직접 C++ (11) 가변 인자 템플릿을 사용할 수

: 당신이 만약 머리처럼, 다른 한편으로

template<typename... Ts> 
struct list{}; 

using l = list<int,char,bool>; 

한 형식에서 다른 형식으로 변환 할 수 있습니다. 이 경우 (기능에 가변 인자) :

template<typename HEAD , typename TAIL> 
struct list{}; 

struct nil{}; 

template<typename... Ts> 
struct make_list; 

template<typename HEAD , typename... TAIL> 
struct make_list<HEAD,TAIL> 
{ 
    using result = list<HEAD,typename make_list<TAIL...>::result; 
}; 

template<> 
struct make_list<> 
{ 
    using result = nil; 
}; 

예 :

template<typename... Ts> 
using make = typename make_list<Ts...>::result; 

using l = make<int,char,bool>; 
+0

나는 variadic 템플릿을 알고 있지만, 더 쉽게하기 때문에 cons리스트를 선호합니다. 함께 일하고 더 빨리. 어쨌든, 당신의 대답은 내 문제에 대한 해결책을 제공하지 않습니다. 자신이 언급했듯이 두 목록 형식간에 변환 할 수 있으므로 매개 변수 목록 목록의 단점 목록에서 질문을 사용하는지 여부는 중요하지 않습니다. –

+0

내 문제의 해결 방법에는 START_TO_LIST, ADD_TO_LIST 및 END_LIST 매크로를 적절하게 정의해야합니다. –

+4

@AmbrozBizjak : 단점 목록은 작동하기가 어렵고 느립니다. 당신은 단지 variadics에 익숙하지 않습니다. –

0

나는 해결책을 발견했습니다!그것은 조금 더 제한되어 있지만; 각 요소에 대해 고유 한 이름을 제공해야하며 요소 수 (여기서는 100)에 상한이 있어야합니다.

#include <type_traits> 

// Good old Cons and Nil. 
template <typename THead, typename TTail> 
struct Cons { 
    using Head = THead; 
    using Tail = TTail; 
}; 
struct Nil {}; 

// Helper template which builds a list from a template 
// providing elements at given indices. 
template <template<int> class ElemAt, int Offset, int Length> 
struct BuildListFromElemAt { 
    using Result = Cons<typename ElemAt<Offset>::Elem, typename BuildListFromElemAt<ElemAt, (Offset + 1), (Length - 1)>::Result>; 
}; 
template <template<int> class ElemAt, int Offset> 
struct BuildListFromElemAt<ElemAt, Offset, 0> { 
    using Result = Nil; 
}; 

// This is where the abuse begins. 
// We use these classes to keep the current length 
// of the list, in combination with function overloads. 
// Each time we add an element, we will provide a more 
// specific overload of a helper function. 
template <int N> 
struct Counter : public Counter<(N - 1)> { 
    static int const Num = N; 
}; 
template <> 
struct Counter<0> { 
    static int const Num = 0; 
}; 

// At the beginning, we define the initial size function 
// taking Counter<0>, and declare the ElemAt template. 
#define BEGIN_LIST(ListName) \ 
Counter<0> ListName##_Size (Counter<0>); \ 
template <int Index> struct ListName##_ElemAt; 

// For each element, we retrieve the current length of the 
// list by checking the return type of calling the size function 
// with a very large Counter. 
// Then we overload the size function for one greater Counter, 
// which ensures that we get an incremented length at the next 
// step. We also define the ElemAt for this index to return the 
// given Value. 
#define ADD_TO_LIST(ListName, Name, Value) \ 
static int const ListName##_##Name##_PrevLen = decltype(ListName##_Size(Counter<100>()))::Num; \ 
static int const ListName##_##Name##_Len = ListName##_##Name##_PrevLen + 1; \ 
Counter<ListName##_##Name##_Len> ListName##_Size (Counter<ListName##_##Name##_Len>); \ 
template <> struct ListName##_ElemAt<ListName##_##Name##_PrevLen> { \ 
    using Elem = Value; \ 
}; 

// At the end, we retrieve the final length of the list, 
// and build the list using the BuildListFromElemAt template. 
#define END_LIST(ListName) \ 
static int const ListName##_Len = decltype(ListName##_Size(Counter<100>()))::Num; \ 
using ListName = typename BuildListFromElemAt<ListName##_ElemAt, 0, ListName##_Len>::Result; 

// Here we go... 
BEGIN_LIST(List) 
ADD_TO_LIST(List, First, int) 
ADD_TO_LIST(List, Second, float) 
ADD_TO_LIST(List, Third, double) 
END_LIST(List) 

// And it works :) 
static_assert(std::is_same<typename List::Head, int>::value, ""); 
static_assert(std::is_same<typename List::Tail::Head, float>::value, ""); 
static_assert(std::is_same<typename List::Tail::Tail::Head, double>::value, ""); 
+0

ADD_TO_LIST에는 템플릿 전문화가 포함되어 있으며 블록 범위에서 선언 할 수 없습니다. 매크로는 기능에서 사용할 수 없습니다. 전역 범위의 형식을 선언하려는 경우 솔루션에 제한이없는 방법이 있습니다. –

+0

지금 내 대답을 업데이트했습니다. 전역, 클래스 및 기능 범위에서 작동합니다. C++은 마술입니다! –