C ++는 컴파일 타임 카운터를 지원합니까?
내부 검사를 위해 일련 번호를 유형 등에 자동으로 할당하고 싶었습니다.
안타깝게도 템플릿 메타 프로그래밍은 기본적으로 기능적 언어이므로 이러한 카운터를 구현할 전역 변수 또는 수정 가능한 상태가 없습니다.
아니면?
요청 별 예제 코드 :
#include <iostream>
int const a = counter_read;
counter_inc;
counter_inc;
counter_inc;
counter_inc;
counter_inc;
int const b = counter_read;
int main() {
std::cout << a << ' ' << b << '\n'; // print "0 5"
counter_inc_t();
counter_inc_t();
counter_inc_t();
std::cout << counter_read << '\n'; // print "8"
struct {
counter_inc_t d1;
char x[ counter_read ];
counter_inc_t d2;
char y[ counter_read ];
} ls;
std::cout << sizeof ls.x << ' ' << sizeof ls.y << '\n'; // print "9 10"
}
음… 예, 템플릿 메타 프로그래밍은 의도 한대로 부작용이 없습니다. 나는 이전 버전의 GCC의 버그와 모든 기능이 가능하다고 믿기 위해 표준의 약간 불분명 한 문구에 오해를 받았습니다.
그러나 최소한 네임 스페이스 범위 기능은 템플릿을 거의 사용하지 않고도 달성 할 수 있습니다. 함수 조회는 아래와 같이 선언 된 함수 집합에서 숫자 상태를 추출 할 수 있습니다.
라이브러리 코드 :
template< size_t n > // This type returns a number through function lookup.
struct cn // The function returns cn<n>.
{ char data[ n + 1 ]; }; // The caller uses (sizeof fn() - 1).
template< typename id, size_t n, size_t acc >
cn< acc > seen( id, cn< n >, cn< acc > ); // Default fallback case.
/* Evaluate the counter by finding the last defined overload.
Each function, when defined, alters the lookup sequence for lower-order
functions. */
#define counter_read( id ) \
( sizeof seen( id(), cn< 1 >(), cn< \
( sizeof seen( id(), cn< 2 >(), cn< \
( sizeof seen( id(), cn< 4 >(), cn< \
( sizeof seen( id(), cn< 8 >(), cn< \
( sizeof seen( id(), cn< 16 >(), cn< \
( sizeof seen( id(), cn< 32 >(), cn< 0 \
/* Add more as desired; trimmed for Stack Overflow code block. */ \
>() ).data - 1 ) \
>() ).data - 1 ) \
>() ).data - 1 ) \
>() ).data - 1 ) \
>() ).data - 1 ) \
>() ).data - 1 )
/* Define a single new function with place-value equal to the bit flipped to 1
by the increment operation.
This is the lowest-magnitude function yet undefined in the current context
of defined higher-magnitude functions. */
#define counter_inc( id ) \
cn< counter_read( id ) + 1 > \
seen( id, cn< ( counter_read( id ) + 1 ) & ~ counter_read( id ) >, \
cn< ( counter_read( id ) + 1 ) & counter_read( id ) > )
빠른 데모 ( 실행 참조 ) :
struct my_cnt {};
int const a = counter_read( my_cnt );
counter_inc( my_cnt );
counter_inc( my_cnt );
counter_inc( my_cnt );
counter_inc( my_cnt );
counter_inc( my_cnt );
int const b = counter_read( my_cnt );
counter_inc( my_cnt );
#include <iostream>
int main() {
std::cout << a << ' ' << b << '\n';
std::cout << counter_read( my_cnt ) << '\n';
}
C ++ 11 업데이트
constexpr
대신 C ++ 11 을 사용하는 업데이트 된 버전 이 sizeof
있습니다.
#define COUNTER_READ_CRUMB( TAG, RANK, ACC ) counter_crumb( TAG(), constant_index< RANK >(), constant_index< ACC >() )
#define COUNTER_READ( TAG ) COUNTER_READ_CRUMB( TAG, 1, COUNTER_READ_CRUMB( TAG, 2, COUNTER_READ_CRUMB( TAG, 4, COUNTER_READ_CRUMB( TAG, 8, \
COUNTER_READ_CRUMB( TAG, 16, COUNTER_READ_CRUMB( TAG, 32, COUNTER_READ_CRUMB( TAG, 64, COUNTER_READ_CRUMB( TAG, 128, 0 ) ) ) ) ) ) ) )
#define COUNTER_INC( TAG ) \
constexpr \
constant_index< COUNTER_READ( TAG ) + 1 > \
counter_crumb( TAG, constant_index< ( COUNTER_READ( TAG ) + 1 ) & ~ COUNTER_READ( TAG ) >, \
constant_index< ( COUNTER_READ( TAG ) + 1 ) & COUNTER_READ( TAG ) > ) { return {}; }
#define COUNTER_LINK_NAMESPACE( NS ) using NS::counter_crumb;
template< std::size_t n >
struct constant_index : std::integral_constant< std::size_t, n > {};
template< typename id, std::size_t rank, std::size_t acc >
constexpr constant_index< acc > counter_crumb( id, constant_index< rank >, constant_index< acc > ) { return {}; } // found by ADL via constant_index
선언은 네임 스페이스 안에 넣어야하며 매크로에서 사용되는 모든 이름 counter_crumb
은 정규화되어야합니다. counter_crumb
템플릿은 ADL와 연결을 통해 발견되는 constant_index
유형.
COUNTER_LINK_NAMESPACE
매크로 여러 네임 스페이스의 범위 내에서 하나 개의 카운터를 증가하기 위해 사용될 수있다.
MSVC와 GCC는 모두 __COUNTER__
단조롭게 증가하는 값을 대신 하는 전 처리기 토큰을 지원한다고 생각합니다 .
나는이 문제를 꽤 오랫동안 해결하려고 생각하고 있었고, 아주 짧은 해결책을 찾았습니다. 적어도 나는 이것을 시도하기 위해 하나의 찬성 투표를 할 자격이 있습니다. :))
다음 라이브러리 코드는 네임 스페이스 수준 기능을 달성합니다. 즉, 내가 구현 성공입니다 counter_read
및 counter_inc
; 그러나 counter_inc_t
( template
클래스가 함수 내에서 허용되지 않기 때문에 함수 내에서 증가 합니다)
template<unsigned int NUM> struct Counter { enum { value = Counter<NUM-1>::value }; };
template<> struct Counter<0> { enum { value = 0 }; };
#define counter_read Counter<__LINE__>::value
#define counter_inc template<> struct Counter<__LINE__> { enum { value = Counter<__LINE__-1>::value + 1}; }
이 기술은 템플릿 메타 프로그래밍을 사용 하고 __LINE__
매크로를 활용합니다 . 참조 결과 답변에서 코드를.
BOOST_PP_COUNTER
Boost.Preprocessor에서 사용할 수 있습니다 .
장점 : 매크로에서도 작동합니다.
단점 : 전체 프로그램에 대해 하나의 "카운터 종류"만 있지만 전용 카운터에 대해 메커니즘을 다시 구현할 수 있습니다.
공유가 배려하고 있기 때문에 나는 기본 예제와 주변 하구 몇 시간 동안 이 쪽이 내 솔루션을 게시 할뿐만 아니라거야 제공합니다.
기사에 링크 된 버전에는 두 가지 주요 단점이 있습니다. 최대 재귀 깊이 (보통 256 정도)로 인해 셀 수있는 최대 수는 매우 낮습니다. 그리고 수백 개 이상의 카운트에 도달하자마자 컴파일하는 데 걸리는 시간은 엄청납니다.
이진 검색을 구현하여 카운터에 대한 플래그가 이미 설정되었는지 여부를 감지하면 최대 개수 (MAX_DEPTH를 통해 제어 가능)를 대폭 늘리고 동시에 컴파일 시간을 개선 할 수 있습니다. =)
사용 예 :
static constexpr int a = counter_id();
static constexpr int b = counter_id();
static constexpr int c = counter_id();
#include <iostream>
int main () {
std::cout << "Value a: " << a << std::endl;
std::cout << "Value b: " << b << std::endl;
std::cout << "Value c: " << c << std::endl;
}
마지막에 예제가 포함 된 완전히 작동하는 코드 : (clang 제외. 주석 참조)
// Number of Bits our counter is using. Lower number faster compile time,
// but less distinct values. With 16 we have 2^16 distinct values.
#define MAX_DEPTH 16
// Used for counting.
template<int N>
struct flag {
friend constexpr int adl_flag(flag<N>);
};
// Used for noting how far down in the binary tree we are.
// depth<0> equales leaf nodes. depth<MAX_DEPTH> equals root node.
template<int N> struct depth {};
// Creating an instance of this struct marks the flag<N> as used.
template<int N>
struct mark {
friend constexpr int adl_flag (flag<N>) {
return N;
}
static constexpr int value = N;
};
// Heart of the expression. The first two functions are for inner nodes and
// the next two for termination at leaf nodes.
// char[noexcept( adl_flag(flag<N>()) ) ? +1 : -1] is valid if flag<N> exists.
template <int D, int N, class = char[noexcept( adl_flag(flag<N>()) ) ? +1 : -1]>
int constexpr binary_search_flag(int, depth<D>, flag<N>,
int next_flag = binary_search_flag(0, depth<D-1>(), flag<N + (1 << (D - 1))>())) {
return next_flag;
}
template <int D, int N>
int constexpr binary_search_flag(float, depth<D>, flag<N>,
int next_flag = binary_search_flag(0, depth<D-1>(), flag<N - (1 << (D - 1))>())) {
return next_flag;
}
template <int N, class = char[noexcept( adl_flag(flag<N>()) ) ? +1 : -1]>
int constexpr binary_search_flag(int, depth<0>, flag<N>) {
return N + 1;
}
template <int N>
int constexpr binary_search_flag(float, depth<0>, flag<N>) {
return N;
}
// The actual expression to call for increasing the count.
template<int next_flag = binary_search_flag(0, depth<MAX_DEPTH-1>(),
flag<(1 << (MAX_DEPTH-1))>())>
int constexpr counter_id(int value = mark<next_flag>::value) {
return value;
}
static constexpr int a = counter_id();
static constexpr int b = counter_id();
static constexpr int c = counter_id();
#include <iostream>
int main () {
std::cout << "Value a: " << a << std::endl;
std::cout << "Value b: " << b << std::endl;
std::cout << "Value c: " << c << std::endl;
}
다른 대안 구현이 있습니다. https://stackoverflow.com/a/6174263/1190123 이 더 좋을 수 있지만, 종이에 몇 가지 증분을 수동으로 작업 한 후에도 여전히 수학 / 필터링을 이해하지 못합니다.
이것은 constexpr 함수 재귀를 사용하여 템플릿이 아닌 선언 된 Highest
함수 의 수를 계산합니다 . __COUNTER__
새로운 선언이 Highest
자체 재귀를 수행 하지 못하도록 방지하는 세대 메커니즘으로 사용됩니다 .
이것은 나를 위해 clang (3.3)에서만 컴파일됩니다. 규정을 준수하는지 잘 모르겠지만 희망적입니다. 일부 구현되지 않은 기능으로 인해 g ++ 4.8이 실패합니다 (오류에 따라). constexpr 버그로 인해 인텔 컴파일러 13도 실패합니다.
256 레벨 카운터
카운터 당 최대 개수는 250 (CounterLimit)입니다. 아래의 LCount 항목을 구현하지 않으면 CounterLimit을 256으로 늘릴 수 있습니다.
이행
#include <iostream>
#include <type_traits>
constexpr unsigned int CounterLimit = 250;
template <unsigned int ValueArg> struct TemplateInt { constexpr static unsigned int Value = ValueArg; };
template <unsigned int GetID, typename, typename TagID>
constexpr unsigned int Highest(TagID, TemplateInt<0>)
{
return 0;
}
template <unsigned int GetID, typename, typename TagID, unsigned int Index>
constexpr unsigned int Highest(TagID, TemplateInt<Index>)
{
return Highest<GetID, void>(TagID(), TemplateInt<Index - 1>());
}
#define GetCount(...) \
Highest<__COUNTER__, void>(__VA_ARGS__(), TemplateInt<CounterLimit>())
#define IncrementCount(TagID) \
template <unsigned int GetID, typename = typename std::enable_if<(GetID > __COUNTER__ + 1)>::type> \
constexpr unsigned int Highest( \
TagID, \
TemplateInt<GetCount(TagID) + 1> Value) \
{ \
return decltype(Value)::Value; \
}
테스팅
struct Counter1 {};
struct Counter2 {};
constexpr unsigned int Read0 = GetCount(Counter1);
constexpr unsigned int Read1 = GetCount(Counter1);
IncrementCount(Counter1);
constexpr unsigned int Read2 = GetCount(Counter1);
IncrementCount(Counter1);
constexpr unsigned int Read3 = GetCount(Counter1);
IncrementCount(Counter1);
constexpr unsigned int Read4 = GetCount(Counter1);
IncrementCount(Counter1);
IncrementCount(Counter2);
constexpr unsigned int Read5 = GetCount(Counter1);
constexpr unsigned int Read6 = GetCount(Counter2);
int main(int, char**)
{
std::cout << "Ending state 0: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<0>()) << std::endl;
std::cout << "Ending state 1: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<1>()) << std::endl;
std::cout << "Ending state 2: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<2>()) << std::endl;
std::cout << "Ending state 3: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<3>()) << std::endl;
std::cout << "Ending state 4: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<4>()) << std::endl;
std::cout << "Ending state 5: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<5>()) << std::endl;
std::cout << Read0 << std::endl;
std::cout << Read1 << std::endl;
std::cout << Read2 << std::endl;
std::cout << Read3 << std::endl;
std::cout << Read4 << std::endl;
std::cout << Read5 << std::endl;
std::cout << Read6 << std::endl;
return 0;
}
산출
Ending state 0: 0
Ending state 1: 1
Ending state 2: 2
Ending state 3: 3
Ending state 4: 4
Ending state 5: 4
0
0
1
2
3
4
1
250 * 250 레벨 카운터
If you want higher values than 256, I think you can combine counters. I did 250 * 250 (although I didn't really test counting past 2). CounterLimit has to be lowered to around 250 for compiler compile time recursion limits. Just to note, this took significantly more time to compile for me.
Implementation
template <typename, unsigned int> struct ExtraCounter { };
template <unsigned int GetID, typename, typename TagID>
constexpr unsigned int LHighest(TagID)
{
return Highest<GetID, void>(ExtraCounter<TagID, CounterLimit>(), TemplateInt<CounterLimit>()) * CounterLimit +
Highest<GetID, void>(
ExtraCounter<TagID, Highest<GetID, void>(ExtraCounter<TagID , CounterLimit>(), TemplateInt<CounterLimit>())>(),
TemplateInt<CounterLimit>());
}
#define GetLCount(TagID) \
LHighest<__COUNTER__, void>(TagID())
#define LIncrementTag_(TagID) \
typename std::conditional< \
GetCount(ExtraCounter<TagID, GetCount(ExtraCounter<TagID, CounterLimit>)>) == CounterLimit - 1, \
ExtraCounter<TagID, CounterLimit>, \
ExtraCounter<TagID, GetCount(ExtraCounter<TagID, CounterLimit>)>>::type
#define IncrementLCount(TagID) \
template <unsigned int GetID, typename = typename std::enable_if<(GetID > __COUNTER__ + 7)>::type> \
constexpr unsigned int Highest( \
LIncrementTag_(TagID), \
TemplateInt<GetCount(LIncrementTag_(TagID)) + 1> Value) \
{ \
return decltype(Value)::Value; \
}
Testing
struct Counter3 {};
constexpr unsigned int Read7 = GetLCount(Counter3);
IncrementLCount(Counter3);
constexpr unsigned int Read8 = GetLCount(Counter3);
Unfortunately, template metaprogramming is essentially a functional language, and as such lacks global variables or modifiable state which would implement such a counter.
Or is it?
C ++는 컴파일 타임 카운터 (즉 __COUNTER__
, __LINE__
앞서 제안한 다른 접근 방식)를 허용하고 각 템플릿 인스턴스에 대해 내부 int 고유 ID를 할당하고 정의 할 수 있습니다. 참조 V1 템플릿 metaprograming는 체인 할당하여 ID와 함께 구현 된 카운터 용액 (V2)을 제 사용 사례를. 두 솔루션 모두 "컴파일시 조밀 한 고유 유형 ID를 어떻게 생성 할 수 있습니까?"에 대한 답변입니다. . 그러나이 작업에는 유일한 ID 할당 자에 대한 중요한 요구 사항이 있습니다.
참고 URL : https://stackoverflow.com/questions/6166337/does-c-support-compile-time-counters
'development' 카테고리의 다른 글
Java com. * 패키지 네임 스페이스 (0) | 2020.12.08 |
---|---|
요소를 숨기지 만 CSS 생성 콘텐츠 표시 (0) | 2020.12.08 |
git push : 마지막 커밋을 제외한 모든 커밋 푸시 (0) | 2020.12.08 |
Android 4.0, 작업 표시 줄의 텍스트가 표시되지 않음 (0) | 2020.12.08 |
Rpresentation Markdown에서 실행되지 않는 코드 포함 (0) | 2020.12.08 |