2009-08-03 2 views
4

문제에 대한 해결책을 생각해 냈습니다. 그러나 항상 컴파일러에서 작동하는지 확실하지 않습니다. 첫째, 문제 : 여러 상황에서 동일한 유형이 주어 졌을 때도 다시 사용될 때마다 인스턴스화되는 템플릿 클래스를 사용하는 것이 바람직하다는 것을 알았습니다 (템플릿 클래스에는 함수 호출로 초기화되는 정적 멤버가 있음). 중요한 부작용이 있습니다. 템플릿을 사용할 때마다이 부작용이 발생하기를 바랍니다.) 이 작업을 수행하는 쉬운 방법은 템플릿 추가 정수 매개 변수를 제공하는 것입니다 :C++ 컴파일 시간 프로그램 고유 번호

template<class T, class U, int uniqueify> 
class foo 
{ 
... 
} 

을하지만 지금 당신은 수동으로 foo를 사용할 때마다 당신이 uniqueify 위해에 다른 값을 전달할 수 있는지 확인해야합니다. 순진 솔루션은이 같은 __LINE__을 사용하는 것입니다

#define MY_MACRO_IMPL(line) foo<line> 
#define MY_MACRO MY_MACRO_IMPL(__LINE__) 

이 솔루션은 비록 문제가있는 - __LINE__ 각 번역 단위에 대한 재설정을 가져옵니다. 따라서 두 개의 번역 단위가 같은 줄에서 템플릿을 사용하면 템플릿은 한 번만 인스턴스화됩니다. 그럴 것 같지 않지만 컴파일러 오류를 디버그하는 것이 얼마나 어려운지 상상해보십시오. 비슷하게 당신은 어떤 식 으로든 파라미터로 __DATE__을 사용할 수 있습니다. 그러나 초 정밀도를 가지고 있으며 컴파일이 시작될 때가 아니라, 그 라인에 도달 할 때가 아닙니다. 따라서 병렬 버전의 make를 사용한다면 두 개의 번역이 가능합니다. 같은 단위의 __DATE__.

또 다른 해결책은 특정 비표준 매크로 __COUNTER__을 사용하는 컴파일러가 있으며 0을 시작하고 사용할 때마다 증가한다는 것입니다. 그러나 동일한 문제가 발생합니다. 전처리 기가 호출 될 때마다 재설정되므로 각 번역 단위가 재설정됩니다.

#define MY_MACRO_IMPL(file, line) foo<T, U, file, line> 
#define MY_MACRO MY_MACRO_IMPL(T, U, __FILE__, __LINE__) 

그러나 외부 연결이 없기 때문에 당신은 표준에 따라 템플릿 매개 변수로 문자 리터럴을 전달할 수 없습니다 :

또 다른 해결책은 함께 __FILE____LINE__을 사용하는 것입니다.

__FILE__에 파일의 절대 경로가 포함되어 있거나 파일 자체의 이름이 표준에 정의되어 있지 않아도 다른 폴더에 동일한 이름의 파일이 두 개있는 경우 단절. 그래서 내 해결책은 다음과 같습니다.

#ifndef toast_unique_id_hpp_INCLUDED 
#define toast_unique_id_hpp_INCLUDED 

namespace { 
namespace toast { 
namespace detail { 

template<int i> 
struct translation_unit_unique { 
    static int globally_unique_var; 
}; 

template<int i> 
int translation_unit_unique<i>::globally_unique_var; 

} 
} 
} 

#define TOAST_UNIQUE_ID_IMPL(line) &toast::detail::translation_unit_unique<line>::globally_unique_var 
#define TOAST_UNIQUE_ID TOAST_UNIQUE_ID_IMPL(__LINE__) 

#endif 

왜이 사용법은 사용 예제가 없으면 처음에는 개요가 명확하지 않습니다. 내가 가진 핵심적인 통찰력은 글로벌 변수 또는 정적 멤버 변수를 만들 때마다 해당 변수의 주소 형태로 프로그램 전체 고유 번호를 생성한다는 것입니다. 그래서 이것은 컴파일 타임에 사용할 수있는 고유 번호를줍니다. __LINE__은 동일한 번역 단위 내에서 충돌을 일으키지 않으며 외부 익명의 네임 스페이스는 변수가 다른 인스턴스 (즉, 다른 주소를 갖게 함)에서 번역 단위를 통과하도록합니다.

사용 예제 : 같은 템플릿에도 불구하고

#include <toast/unique_id.hpp> 

... 

typedef MY_MACRO unique_var; 
typedef MY_MACRO unique_var2; 
unique_var::value = 3; 
unique_var2::value = 4; 
std::cout << unique_var::value << unique_var2::value; 

, 더 차별화 된 매개 변수를 제공하지 않는 사용자는 unique_varunique_var2는 별개 :

template<int* unique_id> 
struct special_var 
{ 
    static int value; 
} 

template<int* unique_id> 
int special_var<unique_id>::value = someSideEffect(); 

#define MY_MACRO_IMPL(unique_id) special_var<unique_id> 
#define MY_MACRO MY_MACRO_IMPL(TOAST_UNIQUE_ID) 

그리고 foo.cpp에이된다.

나는 컴파일 타임에 실제로 사용할 수있는 익명 네임 스페이스의 변수 주소가 대부분 걱정된다. 기술적으로 익명의 네임 스페이스는 내부 연결을 선언하는 것과 같으며 템플릿 매개 변수는 내부 연결을 가질 수 없습니다. 그러나 표준에서 익명의 네임 스페이스를 처리하는 방법은 변수가 프로그램 전체의 고유 한 이름을 가진 네임 스페이스의 일부로 선언 된 것과 같습니다. 기술적으로는 이 일반적으로 생각하지는 않지만 외부 연결을 사용합니다. 그것의 그것 같이. 표준이 제 편이라고 생각합니다. 그러나 확실하지 않습니다.

나는이 유용 할 이유을 설명하는 가장 좋은 일을 한 경우 모르겠지만이 토론을 위해서, 그것은, 내가 맹세)

답변

1

이 안전해야 - 그러나 더 간단한 방법은 FILE을 사용하는 것입니다. 또한 int는 64 비트 플랫폼에서는 충분하지 않습니다. intptr_t를 사용하십시오 :

template<const char *file, int line> 
class unique_value { 
    static char dummy; 
    unique_value() { } 
public: 
    static intptr_t value() { return (intptr_t)&dummy; } 
}; 

#define UNIQUE_VALUE (unique_value<__FILE__, __LINE__>::value()) 

또한 매크로 또는 템플릿 내에서 사용하면 분해됩니다.

또한 부작용이있는 정적 값을 가진 템플릿은 나쁜 생각입니다. main()이 호출되기 전에 부작용이 임의의 순서로 발생한다는 것을 기억하십시오. 무작위 함수에 초기화 부작용을 묻는 것이 유지 관리에 좋지 않습니다.

+0

조셉은 이미 '__FILE__'을 고려했지만 문자열 리터럴은 내부 링크가 있고 매크로는 반드시 전체 경로를 포함하지는 않으므로 거부합니다. 충분히 독특해야한다. 그래서, 그 우려 중 어느 것도 유효하지 않다고 말하고 있습니까? –

+0

그래, 실제로 __FILE__을 사용하려고하면 GCC는 적어도 그것을 거부 할 것입니다. 필자는 컴파일러를 따르는 모든 표준이 가능할 것이라고 생각하지만, MSVC와 같은 다른 대중적인 컴파일러가이 점에 부합하는 표준인지 여부는 알 수 없습니다. –

+0

아마도 C++ 11에서 사용자 정의 리터럴 및 constexpr 생성자를 사용하여이 작업을 수행 할 수는 있지만 아직 시도하지 않았습니다. –

1

이 기술은 일반적으로 두 가지 이유로 안전하지 않습니다.

  1. __LINE__

    동일한 변환 부에서 두 개의 다른 라인 동일 할 수 있습니다 여러 헤더 파일의 같은 행 번호 사용을 통해 (흔히) #line 지시 또는 관통.

  2. TOAST_UNIQUE_ID 또는 헤더 파일의 인라인 함수 또는 템플릿 정의에서 파생 된 내용을 사용하는 경우 ODR 위반이 발생합니다. 당신이 헤더 파일이 사용하지 않습니다, 그리고 메인 소스 파일에 #line를 사용하지 않으며, 줄에, 안전하게 보인다 번만 매크로를 사용하는 경우 말했다

. (__LINE__에서 __COUNTER__으로 전환하여 해당 제한을 제거 할 수 있습니다.