2012-11-01 2 views
12

는 ++ 템플릿 메타 프로그래밍을 C를 사용하여 컴파일시에 클래스 멤버의 오프셋 (offset)를 계산하기컴파일 타임에 클래스 멤버의 오프셋을 계산하는 방법은 무엇입니까?

class A 
{ 
    public: 
    //methods definition 
    .... 

    private: 
    int i; 
    char *str; 
    .... 
} 

C++에서 클래스 정의가 가능 주어진? 클래스는 POD가 아니며 가상 메소드, 기본 및 객체 데이터 멤버를 가질 수 있습니다.

+0

"반원들의 상쇄"란 정확히 무엇을 의미합니까? 멤버에게 전달하기 위해 클래스의 인스턴스에 대한 포인터에 추가 할 바이트 수 (예 :'reinterpret_cast''를'char *'라고 부름)를 의미합니까? 그렇다면 간단한 빼기가 말하지 않습니까? –

+0

이러한 유형에 대해 정의 된 경우'offsetof (A, i)'를 사용할 수 있습니다. 컴파일러 설명서를 확인하십시오. –

+0

다음은 [link] (http://www.cplusplus.com/reference/clibrary/cstddef/offsetof/)에서 offsetof()를 사용하는 예제 코드입니다. – Hindol

답변

2

아니요, 일반적으로 아닙니다.

offsetof 매크로는 POD (일반 오래된 데이터) 구조체 용으로 존재하며 C++ 0x를 표준 레이아웃 구조체 (또는 다른 유사한 약간의 확장자)로 약간 확장 할 수 있습니다. 제한된 경우에는 해결책이 있습니다.

C++은 컴파일러 작성자에게 많은 자유를 제공합니다. 어떤 클래스가 클래스의 멤버에게 가변 오프셋을 가지지 못하게하는 절을 알지 못합니다. 그러나 컴파일러가 왜 그렇게하는지 확신 할 수 없습니다. ;)

코드 표준을 준수하면서도 여전히 오프셋을 유지하는 한 가지 방법은 offsetof가 작동하는 POD (또는 일부 C++ 0x 확장) 하위 구조에 데이터를 저장하는 것입니다. 그런 다음 전체 클래스 대신 하위 구조체에서 작업하십시오. 또는 표준 준수를 항복 할 수 있습니다. 클래스 내에서 구조체의 오프셋을 알 수는 없지만 구조체 내의 멤버 오프셋을 알 수 있습니다.

중요한 질문은 "왜 내가 이것을 원하고 좋은 이유가 있습니까?"입니다.

+0

정말 좋은 이유는 아닐지 모르지만 일부 데이터베이스 상호 작용은 인덱스를 생성하기 위해 데이터 구조에 컴파일시 상수 오프셋이 필요합니다. 그것은 하드 코딩이 가능하지만 컴파일러가 대신 계산하여 일부 오류를 피할 수 있습니다. –

+1

@austin 비표준 레이아웃 클래스를 데이터베이스에 넣는 것은 나쁜 생각 일 것입니다. – Yakk

4

음 ... C++ 11에서는 일반 C++ 기능 (즉, 특정 컴파일러 내장 함수에 위임하지 않고)을 통해 이러한 오프셋을 실제로 계산할 수 있습니다. liveworkspace 행동에

:

template <typename T, typename U> 
constexpr int func(T const& t, U T::* a) { 
    return (char const*)&t - (char const*)&(t.*a); 
} 

그러나이 t은 모든 클래스에 적용되지 않을 수도 여기 constexpr 인스턴스에 대한 참조 인에 의존한다. 생성자 인 한 virtual 메서드를 사용하면 T이 생성되지 않으며 생성자도 생성 할 수 없습니다.

그래도 이것은 상당히 방해가됩니다. 평가되지 않은 상황에서는 실제로는 std::declval<T>()을 사용하여 객체를 시뮬레이션 할 수 있습니다. 없음. 따라서 객체의 생성자에 대한 특별한 요구 사항이 없습니다. 다른 한편으로, 우리가 그런 맥락에서 추출 할 수있는 가치는 거의 없다 ... 그리고 그들은 현재의 컴파일러에 대해서도 문제를 제기한다. 자, 가짜로 만들자! 행동

liveworkspace에서 :

template <typename T, typename U> 
constexpr size_t offsetof_impl(T const* t, U T::* a) { 
    return (char const*)t - (char const*)&(t->*a) >= 0 ? 
      (char const*)t - (char const*)&(t->*a)  : 
      (char const*)&(t->*a) - (char const*)t; 
} 

#define offsetof(Type_, Attr_)       \ 
    offsetof_impl((Type_ const*)nullptr, &Type_::Attr_) 

내가 예견 유일한 문제는 기본 오브젝트의 그것의 실행 위치의, virtual 상속입니다. 나는 다른 결함이 있다면 기꺼이 받게 될 것입니다.

+1

또한 기본 제공 offsetof 매크로도 참조하십시오 – markd

+0

@markd : [주의] (http://en.cppreference.com/w/cpp/types/offsetof) => 유형이 표준 레이아웃 유형이 아니거나 구성원이 정적 데이터 멤버 또는 함수 멤버 인 경우, C 표준에 지정된대로 'offsetof' 매크로를 사용하는 동작은 정의되지 않습니다. 바꾸어 말하면, C의'offsetof' 매크로는 C와 같은 유형에서만 작동합니다. –

+0

왜'T *'와'char *'대신에'T const *'와'char const *'를 사용하고 있습니까? – ens

8

Matthieu M.의 대답하지만 짧은없이 매크로 :

template<typename T, typename U> constexpr size_t offsetOf(U T::*member) 
{ 
    return (char*)&((T*)nullptr->*member) - (char*)nullptr; 
} 

그리고는이 같은라고 :

struct X { int a, b, c, d; } 

std::cout << "offset of c in X == " << offsetOf(&X::c); 

편집 :

제이슨 쌀이 올바른 것입니다. C++ 11에서 실제 상수 표현식을 생성하지 않습니다. http://en.cppreference.com/w/cpp/language/constant_expression의 제한 사항을 고려하면 포인터 모양의 차이가없고 특히 reinterpret_cast이 상수 표현식에있을 수 있습니다. 스탠리 B. 리프만, 원래 C++ 디자이너 중 하나가 쓴, "는 C++ 객체 모델 내부"의 1996 년 책에서

+0

철자를 쓰려면 U는 멤버의 유형이고 T는 구조체의 유형입니다. 나는 그 사람들을 처음으로 변색했다. –

+0

@Glen Low :'- (char *) nullptr' 부분이 정말로 필요합니까? 우리는 단지 0을 빼는 것이 아닌가? 나는 우리가 그것을 더 짧게 만들 수 있다고 생각한다 : – nav

+1

@nav gcc에서는 작동하지만, llvm에서는 작동하지 않는 것 같다 : error : 'char *'타입의 rvalue 인 'size_t'(일명 'unsigned int') 타입의 반환 객체를 초기화 할 수 없다. –

0

4.4

값이 반환 장에서 포인터 - 투 - 회원 기능을 참조한다 nonstatic 데이터 멤버의 주소를 취하는 것은 클래스 레이아웃에서 멤버의 위치 바이트 값 (1을 더한 값)입니다. 하나는 불완전한 가치라고 생각할 수 있습니다. 멤버의 실제 인스턴스에 액세스하려면 먼저 클래스 객체의 주소에 바인딩되어야합니다.

이전 삶에서 어딘가에서 +1하는 것을 막연하게 기억하지만 나는이 구문을 처음 보거나 사용하지 못했습니다.

class t 
{ 
public: 
    int i; 
    int j; 
}; 
int (t::*pmf)() = &t::i; 

는 최소한의 설명에 따르면,이 "거의"오프셋을 얻을 수있는 멋진 방법이 될 것으로 보인다.

하지만 적어도 GCC에서는 더 이상 작동하지 않는 것 같습니다. 나는

여기에 무슨 일이 일어나고 있는지에 대한 역사가 있습니까? 이 같은 것이 아직도 가능합니까?

웹 문제 - 폐기 된 도서는 절대로 죽지 않습니다 ...

관련 문제