2010-07-05 4 views
1

유형 T와 구조체가 T 유형의 유일한 균일 한 요소 만 갖는 경우가 있습니다.배열 첨자 연산자를 사용하여 구조체 멤버에 액세스

struct Foo { 
    T one, 
    T two, 
    T three 
}; 

나는 fallowing 방식으로 접근하고 싶습니다이 :

struct Foo { 
    T one, 
    T two, 
    T three 

    T &operator [] (int i) 
    { 
     return *(T*)((size_t)this + i * cpp_offsetof(Foo, two)); 
    } 
}; 

매크로 cpp_offsetof가 (이 올바른 것으로 간주됩니다)입니다 : '

#define cpp_offsetof(s, m) (((size_t)&reinterpret_cast<const volatile char&>((((s*)(char*)8)->m))) - 8) 

C++ 표준 아무튼 그것을 보장 할 수는 있지만, 멤버가 고정 된 오프셋으로 멀리 떨어져 있다고 가정 할 수 있으며 위에있는 것은 올바른 크로스 플랫폼 솔루션입니까?


100 % 호환 솔루션은 다음과 같습니다

struct Foo { 
    T one, 
    T two, 
    T three 

    T &operator [] (int i) { 
     const size_t offsets[] = { cpp_offsetof(Foo, one), cpp_offsetof(Foo, two), cpp_offsetof(Foo, three) }; 
     return *(T*)((size_t)this + offsets[i]); 
    } 
}; 

[편집] 표준을 준수하고 빠른 버전은snk_kid using pointers to data members[/ 편집]
제시하지만 추가 조회 테이블을 필요로 한 나는 피하려고 노력하고있어.

// 편집
그리고 또 하나. 나는 배열과 상수를 사용하여 이러한 필드를 인덱싱 할 수 없으며 구조체의 필드 이름을 지정해야합니다 (일부 매크로 필요).

// EDIT2
왜 구조체의 필드 이름을 지정해야합니까? 매크로는 무엇입니까? 더 큰 프로젝트의 설정 시스템입니다. 단순화는 다음과 같습니다.

struct Foo { 
    int one; 
    int two; 
} 
foo; 

struct Setting { void *obj, size_t filed_offset, const char *name, FieldType type } 

#define SETTING(CLASS, OBJ, FIELD, TYPE) { OBJ, cpp_offsetof(CLASS, FIELD), #OBJ #FIELD, TYPE } 

Setting settings[] = { 
    SETTING(Foo, foo, one, INT_FIELD), 
    SETTING(Foo, foo, two, INT_FIELD) 
}; 

그리고 다시 : 저는 100 % 호환 솔루션을 찾고 있지만 99 %는 아닙니다. 필자는 어떤 컴파일러가 균일 한 필드 사이에 비 균일 패딩을 배치 할 것으로 기대할 수 있는지 묻습니다.

+0

OP와 관련이 없지만 offsetof 매크로가 8을 기본으로 사용하는 이유를 아는 사람이 있습니까? MSVC는 0을 사용하고, GCC는 내장 함수를 사용하며 심지어 0이 아닌 기반에 ICE 버그를 가지고 있습니다. –

+0

대답이 있더라도, 나는이 질문에서 벗어나 모든 것이 어떻게 흐트러지고 있는지 보게 될 것입니다 ...: S (농담, 당연히) –

+0

아니, 나는 그것을 직접 확인하고 있었다. 내 의견을 읽으십시오. 지금 나는 내 downwote를 취소했다. 내가 틀렸다는 것을 증명하지 않으면, downvote가있을 것입니다. 그리고 분명히하기 위해, 나는 테이블 룩업에 대한 해결책을 이미 알고 있다고 썼다. 그래서 나는 downvoted했다. – adf88

답변

0

컴파일러가 패딩을 허용하기 위해 구성원간에 데드 바이트를 추가 할 수 있기 때문에 사용자가이를 수행 할 수 없습니다.

원하는대로하려면 두 가지 방법이 있습니다.

첫 번째는 컴파일러가 패딩 바이트를 추가하지 않도록 컴파일러 관련 키워드 또는 pragma 매크로를 사용하는 것입니다. 하지만 그건 이식성이 아닙니다. 매크로 요구 사항을 충족시키는 것이 가장 쉬운 방법 일 수 있으므로 다른 컴파일러를 사용할 때이 가능성을 탐색하고 더 많은 pragma를 추가 할 준비를하는 것이 좋습니다.

다른 방법은 먼저 접근을 한 후, 당신의 멤버가 정렬되어 있는지 확인 추가하는 것입니다

struct Foo { 

    T members[ 3 ]; // arrays are guarrantied to be contigu 


    T& one() { return members[0]; } 
    const T& one() const { return members[0]; } 
    //etc... 

}; 
+0

첫째, 필딩 (추가 된 경우)이 각 필드에 고정 될 것이라고 가정 할 수 있는지 묻습니다. 둘째, 크로스 플랫폼 솔루션을 요구하고 있습니다. 셋째, 필드가 아니라 메소드가 있어야합니다. – adf88

+0

다음은 C++에서 불가능합니다 AFAIK – Klaim

+3

마지막 요구 사항은 다른 가능성을 없앱니다. 어쩌면 매크로를 추가하여 매크로를 사용하여 수업을 진행할 수는 있지만 실제로는 스타일과 악조건이 좋습니다. – Klaim

6

NON-POD 유형과 가상 멤버 함수를 사용하는 등 그 작동하지 않습니다 코드입니다. 그냥 포인터의 테이블마다 데이터 - 귀하는 const를 사용할 수 있는지 확인

template< typename T > 
struct Foo { 

    typedef size_t size_type; 

private: 

    typedef T Foo<T>::* const vec[3]; 

    static const vec v; 

public: 

    T one; 
    T two; 
    T three; 

    const T& operator[](size_type i) const { 
     return this->*v[i]; 
    } 

    T& operator[](size_type i) { 
     return this->*v[i]; 
    } 
}; 

template< typename T > 
const typename Foo<T>::vec Foo<T>::v = { &Foo<T>::one, &Foo<T>::two, &Foo<T>::three }; 

: 당신이 뭘 하려는지 달성하기 위해 표준 규격 (효율적인) 방법은 데이터 멤버에 포인터를 사용하여,이 회원은 최적화를 얻을 수 있습니다. 내가 무슨 말을하고 있는지 보려면 here을 확인하십시오.

+0

표준 준수 솔루션을 제공 한 이유는 무엇인가? –

+0

-1 나도 내 질문에 왜 나 한테 쓴거야? – adf88

+1

@ adf88 : 귀하의 질문에 snk_kid가 쓴 것과 같은 것은 무엇입니까? –

1

달성하려는 대상이 여전히 컴파일 타임 기능인 경우 다른 방법은 템플릿 전문화입니다.

class Foo { 
    T one; 
    T two; 
    T three; 
}; 

template <int i> T & get(Foo& foo); 

template T& get<1>(Foo& foo){ return foo.one;} 
template T& get<2>(Foo& foo){ return foo.two;} 
template T& get<3>(Foo& foo){ return foo.three;} 

멤버 함수로 얻을 정의하는 것이 좋을 것이다하지만 템플릿 멤버 함수를 전문으로하지 수 있습니다. 이제 이것이 단지 컴파일 시간 인 경우 확장을 찾고 있다면 이것은 룩업 테이블 이전 게시물 중 하나의 발행을 피할 것입니다. 런타임 해상도가 인 경우에는 룩업 테이블이 필요합니다.

- 브래드 펠란 http://xtargets.heroku.com

+0

Brad, 나는 들어가서 코드를 컴파일 할 수있게 만들었습니다. 내가 그것을 날려 버리면, 뒤로 굴러 라. – sbi

+0

또한'class Foo {template get() {return :: get (* this);}};은 멤버 함수를 처리해야합니다. – sbi

+0

편집하는 데 아무런 문제가 없습니다. 컴파일러없이 올바른 C++를 작성하는 것이 항상 당신을 위해 철자 검사를합니다! – bradgonesurfing

1

당신은 다양한에 데이터 (그래서 당신은 조회 테이블을 사용하지 않고 인덱스 액세스 할 수 있습니다)와 가진 참조를 유지하는 배열을 사용하여 원하는 것을 달성 할 수있을 것 배열 요소 (매크로에서 사용할 수 있도록 '명명 된'요소를 가질 수 있습니다).

매크로가 무엇이 필요한지 잘 모르겠습니다. 그렇기 때문에이 방법이 정상적으로 작동하는지 100 % 확신하지는 못합니다. 또한 조회 테이블 접근법의 약간의 오버 헤드가 피하기 위해 너무 많은 농구를 뛰어 넘을 가치가 있는지 확신하지 못합니다.

#include <stdio.h> 

template< typename T > 
struct Foo { 

private:  
    T data_[3]; 

public: 

    T& one; 
    T& two; 
    T& three; 

    const T& operator[](size_t i) const { 
     return data_[i]; 
    } 

    T& operator[](size_t i) { 
     return data_[i]; 
    } 

    Foo() : 
     one(data_[0]), 
     two(data_[1]), 
     three(data_[2]) 
     {}; 

}; 


int main() 
{ 
    Foo<int> foo; 

    foo[0] = 11; 
    foo[1] = 22; 
    foo[2] = 33; 

    printf("%d, %d, %d\n", foo.one, foo.two, foo.three); 

    Foo<int> const cfoo(foo); 

    printf("%d, %d, %d\n", cfoo[0], cfoo[1], cfoo[2]); 

    return 0; 
} 
0

당신이 컴파일러 확실 경우 : 다른 한편으로는, 그래서 여기 당신의 고려 사항을 위해, 나는 여기에 제안 접근 방식은 더 이상 복잡한 테이블의 점슛 방식보다 생각하지 않는다 당신이 사용하는 올바른 코드를 생성 할 것입니다 (그리고 나는 그들이 T가 참조 형식이 어쨌든 가정하지 않을 것이라고 상상할 것입니다) 최선의 일은 구조체가 배치되어 있는지 확인 일종의 넣어 네가 생각 한대로. 동일한 유형의 인접한 멤버간에 비 균일 패딩을 삽입하는 특별한 이유는 생각할 수 없지만 구조 레이아웃을 손으로 확인하면 발생하는 경우 최소한 알 수 있습니다.

구조체 (S)가 정확히 N 형 T의 멤버는, 예를 들어, 당신은 그들이 단단히 포장되어 컴파일시에 확인할 수있는 경우

단순히 sizeof를 사용하여 :

struct S { 
    T a,b,c; 
}; 

extern const char check_S_size[sizeof(S)==3*sizeof(T)?1:-1]; 

을이 컴파일되면, 그들은 ' 다시는 단단히 채워 져야합니다. 다른 어떤 공간도 없기 때문입니다. 당신은 당신이 다른 후 직접 하나를 배치되도록 할 것인지, N 회원이 일어날 경우

, 당신은 offsetof를 사용하여 비슷한 작업을 수행 할 수 있습니다

class S { 
    char x; 
    T a,b,c; 
}; 

extern const char check_b_offset[offsetof(S,b)==offsetof(S,a)+sizeof(T)?1:-1]; 
extern const char check_c_offset[offsetof(S,c)==offsetof(S,b)+sizeof(T)?1:-1]; 

을 컴파일러에 따라이해야 할 수도 있습니다 런타임 체크가 될 가능성이 있으며, 아마도 offsetof을 사용하지 않을 것입니다. 어쨌든 offsetof이 정의되지 않았기 때문에 비 POD 유형에 대해 수행하려고 할 수 있습니다.

S tmp; 
assert(&tmp.b==&tmp.a+1); 
assert(&tmp.c==&tmp.b+1); 

이것은 실패 시작 주장하는 경우 수행 할 작업에 대한 아무 말도하지 않지만, 적어도 가정이 진실하지 않은 몇 가지 경고를해야한다 ... (그런데

,

관련 문제