2012-02-13 5 views
2

내가 회원의 무리와 함께 구조체를 말한다. 이걸 님의 타입으로 인코딩하고 싶습니다. 그래서 같은 :템플릿을 사용하여 멤버간에 제약 조건을 표현하는 방법은 무엇입니까?</p> <pre><code>struct foo { int len; bar *stuff; }; </code></pre> <p>이 너무 오래 <code>len</code>입니다 <code>bar</code>의 배열을 가리 킵니다 <code>stuff</code>을 공교롭게도 :

struct foo { 
    int len; 
    DependentLength<bar, &foo::len> stuff; 
}; 

는 그럼 난 바 배열에 대한 포인터처럼 행동하는 DependentLength을 구현할 수 있지만, foo::len보다 큰 인덱스에보고하려고 할 때 그 주장한다. 그러나 operator []는 하나의 매개 변수 인 인덱스 만 사용하고 멤버 포인터 템플릿 매개 변수를 역 참조하고 어설 션 검사를 수행하려면 'foo'객체의 위치를 ​​알아야하기 때문에 DependentLength<&foo::len>::operator[]을 구현할 수 없습니다.

그러나 DependentLength는 'foo'의 멤버로만 사용됩니다. 내가 정말로하고 싶은 것은 DependentLength가 foo 포인터에 상대적이 아닌 인 len 을 어디에서 찾을 수 있는지를 알려주는 것입니다. 그래서 DependentLength<(char*)&foo::stuff - (char*)&foo::len> stuff;과 비슷하지만, 합법적 인 C++가 아닙니다. 이 작업을 수행 할 수있는 악의적 인 언어 해킹이 잘못되었거나 실패 했습니까?

+0

왜 처음에는'std :: vector'와 같은 컨테이너를 사용하지 않으시겠습니까? –

+2

릴레이션을 뒤집습니다 :'struct foo {std :: vector bar; int len ​​() const {return bar.size(); }}; ' –

+0

실제로 와이어에서 값을 가져 오는 패키지 구조체의 컨텍스트에서이 것을보고 있으므로 레이아웃을 다시 정렬 할 수는 없습니다. –

답변

3

그래서 DependentLength<(char*)&foo::stuff - (char*)&foo::len> stuff;

당신은 실행 시간 동안 그들에게 전달 된 동적 속성 ... 템플릿에 대해 작동하지 않습니다 기반으로 계산을 수행 할 템플릿을 요구하고 같은 그들이 인스턴스화해야하기 때문에 컴파일시 템플릿 매개 변수로 요청한 코드를 compile time에 만들 수있는 값이 있습니다. 따라서 템플릿에 전달 된 모든 값은 런타임이 아닌 컴파일시 해석 가능해야합니다.

동적 컨테이너 유형을 사용해야합니다. 예를 들어 std::vector은 기본 컨테이너의 경계를 초과하는 경우 std::vector::at() 함수가 예외를 throw하는 요청을 처리합니다. 불행하게도 static_assert만큼 편리하지는 않지만 다시 경계를 위해 런타임 검사가 필요하기 때문에 static_assert을 사용하는 것은 불가능합니다. 또한 std::vector에는 operator[]에 대한 오버로드, 반복기, 크기에 대한 쿼리 등이 통합되어 있습니다.

+0

템플릿에 런타임 값을 작동하도록 요청하지 않습니다. & foo :: len과 & foo :: stuff는 둘 다 컴파일 타임 상수입니다. 그것들은 절대 주소가 아닌 오프셋입니다. –

2

템플릿에 길이로 사용할 오프셋을 지정할 수 있습니다.

template<typename T, typename LEN_T, ptrdiff_t LEN_OFFSET> 
class DependentArray 
{ 
public: 
    T& operator[](LEN_T i_offset) 
    { 
    if (i_offset < 0) throw xxx; 
    if (i_offset > this->size()) throw xxx; 
    return this->m_pArr[i_offset]; 
    } // [] 
private: 
    LEN_T& size() 
    { 
    return *reinterpret_cast<LEN_T*>(reinterpret_cast<char*>(this) + LEN_OFFSET); 
    } //() 
private: 
    T* m_pArr; 
}; 

struct foo 
{ 
    int len; 
    DependentArray<bar, int, -sizeof(int)> stuff; 
}; 

편집 2 : 다른 솔루션의

생각. 이 만드는

#define MEMBER_OFFSET(T,M) \ 
    (reinterpret_cast<char*>(&reinterpret_cast<T*>(0x10)->M) - \ 
    reinterpret_cast<char*>(reinterpret_cast<T*>(0x10))) 

template<typename T, typename LEN_T, typename SIZE_OFFSET_SUPPLIER> 
class FooDependentArray 
{ 
public: 
    T& operator[](LEN_T i_offset) 
    { 
    if (i_offset < 0) throw xxx; 
    if (i_offset > this->size()) throw xxx; 
    return this->m_pArr[i_offset]; 
    } // [] 
private: 
    LEN_T& size() 
    { 
    const ptrdiff_t len_offest = SIZE_OFFSET_SUPPLIER::getOffset(); 

    return *reinterpret_cast<LEN_T*>(reinterpret_cast<char*>(this) + len_offset); 
    } //() 
private: 
    T* m_pArr; 
}; 

struct FooSizeOffsetSupplier 
{ 
    static ptrdiff_t getOffset(); 
}; 

struct foo 
{ 
    int len; 
    DependentArray<bar, int, FooSizeOffsetSupplier> stuff; 
}; 

ptrdiff_t FooSizeOffsetSupplier::getOffset() 
{ 
    return MEMBER_OFFSET(Foo,m_len) - MEMBER_OFFSET(Foo,m_pArr); 
} //() 

이 가능 foo에 구성원을 추가 및 제거 : foo는 단지 크기 필드의 오프셋을 공급하고 그 방법을 정의하는 foo를 정의하고 오프셋을 계산할 수있다 후에하는 좋은 클래스를 사용합니다.

+0

'size()'멤버 함수에서, 임의의 값을 포인터 타입으로 형변환 한 후 역 참조하는 것이 어떻게 안전할까요? 또한 임시 객체에 대한 참조를 반환하는 것은 결코 안전하지 않습니다. 나는 그 포인터를'LEN_T * '보다는'LEN_T'에 던지기를 원하고,'LEN_T'보다는'LEN_T'를 반환하기를 원한다고 생각합니다. – Jason

+0

그게 내가 지금까지 가지고 있지만 구조체에 새 멤버를 추가 할 때마다 수동으로 템플릿 매개 변수를 변경하지 않아도되기를 바랬습니다. –

+0

@ Jason : 그것은 임의적이지 않습니다. 그는 'this'를 char *에 캐스팅하여 포인터 산술 연산을 바이트 단위로 처리 한 다음 foo 구조체에서 DependentArray 앞에 오는 int를 찾기 위해 4 바이트를 백업합니다. –