문제

나는 이것이 가능하다고 확신한다. 왜냐하면 나는 그것을 본 것을 확신하기 때문이다. 나는 그것이 굉장하다고 생각하지만, 나는 "이것은 ____이기 때문에 끔찍한 아이디어"라는 줄을 따라 기꺼이 답을 받아 들일 것입니다.

기본 구조물이 있다고 말합니다.

struct vertex
{
    float x, y, z;
};

이제 이러한 변수에 대한 별명을 구현하고 싶습니다.

vertex pos;
vertex col;
vertex arr;

pos.x = 0.0f; pos.y = 0.5f; pos.z = 1.0f;
col.r = 0.0f; col.g = 0.5f; col.b = 1.0f;
arr[0] = 0.0f; arr[1] = 0.5f; arr[2] = 1.0f;

이상적으로 세 번째 구문은 배열과 구별 할 수 없습니다. 즉, 내가 보냈다면 arr 데이터를 저장할 플로트 배열을 기대하는 함수에 대한 참조 매개 변수로 glGet 함수), 잘 작동합니다.

어떻게 생각해? 가능한? 가능하지만 어리석은가?

도움이 되었습니까?

해결책

내가 할 일은 접근자를 만드는 것입니다.

struct Vertex {
    float& r() { return values[0]; }
    float& g() { return values[1]; }
    float& b() { return values[2]; }

    float& x() { return values[0]; }
    float& y() { return values[1]; }
    float& z() { return values[2]; }

    float  operator [] (unsigned i) const { return this->values_[i]; }
    float& operator [] (unsigned i)       { return this->values_[i]; }
    operator float*() const { return this->values_; }

private:
    float[3] values_;
}

다른 팁

유니온의 이름이없는 중첩 구조는 표준 C ++가 아닙니다. 그러나 이것은 작동해야합니다.

struct Vertex
{
private:
   typedef float Vertex::* const vert[3];
   static const vert v;

public:
   typedef size_t size_type;
   float x, y, z;

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

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

};

const Vertex::vert Vertex::v = {&Vertex::x, &Vertex::y, &Vertex::z};

편집 : 조금 더 많은 정보. Struct는 3 개의 포인터-데이터 멤버 배열을 사용하여 과부하 된 [] 연산자의 데이터에 액세스합니다.

"typedef float vertex ::* const vert"라인은 vert가 정점 구조물의 플로트 멤버에 대한 포인터임을 의미합니다. [3] 은이 중 3 개 배열임을 의미합니다. 오버로드 된 연산자 [] 에서이 배열은 색인화되고 포인터 간 데이터 멤버가 해석되고 값이 반환됩니다.

또한이 방법은 포장 문제에 관계없이 작동해야합니다. 컴파일러는 정점 구조를 자유롭게 채울 수 있지만 좋아하지만 여전히 잘 작동합니다. 플로트가 다르게 포장되면 익명 노동 조합이 문제가 발생합니다.

노조를 사용 하시겠습니까?

union vertex
{
    struct { float x, y, z; };
    struct { float r, g, b; };
    float arr[3];
};

나는 그것을 추천하지 않을 것입니다 - 그것은 혼란을 초래할 것입니다.


추가:

Adrian이 그의 답변에서 언급 한 바와 같이, 익명 구조 회원과 의이 연합은 ISO C ++에 의해 지원되지 않습니다. 그것은 GNU G ++에서 작동합니다 (켜질 때 지원되지 않는 것에 대한 불만이 있습니다. '-Wall -ansi -pedantic'). 구조 요소 이름 이름이 모든 구조에서 고유 해야하는 경우, 프리-스탠다드 C 일 (Pre-K & R 1st EDN)을 연상시키고 계약 된 표기법을 사용하여 구조 내에서 오프셋을 얻을 수 있습니다. 다른 구조 유형에서 멤버 이름을 사용하여 무정부 상태입니다. 내가 C를 사용하기 시작했을 때 (오래 전부터 K & R1) 이미 역사적 사용이었다.

익명 노조 구성원 (두 구조의 경우)과 함께 표시된 표기법은 C11 (ISO/IEC 9899 : 2011)에 의해 지원되지만 이전 버전의 C 표준에 의해서는 지원되지 않습니다. ISO/IEC 14882 : 2011 (C ++ 11)의 섹션 9.5는 익명 노조를 제공하지만 GNU g++ (4.9.1)은 표시된 코드를 수락하지 않습니다. -pedantic, 식별 "warning: ISO C++ prohibits anonymous structs [-Wpedantic]".

아이디어가 혼란을 초래할 것이기 때문에, 나는 그것이 표준이 아니라고 특별히 걱정하지 않습니다. 나는이 작업에 대한 메커니즘을 사용하지 않을 것입니다 (그리고 유익하더라도 익명 구조를 노동 조합에서 사용하는 것이 좋습니다).


우려 사항이 제기되었습니다.

세 가지 (XYZ, RGB 및 배열)가 반드시 정렬되지는 않습니다.

그것은 세 가지 요소를 가진 연합입니다. 세 요소는 같은 주소에서 시작합니다. 처음 두 개는 3 개의 플로트 값을 포함하는 구조입니다. 상속이 없으며 다른 레이아웃 등을 제공 할 가상 기능이 없습니다. 구조는 세 가지 요소로 연속적으로 배치됩니다 (실제로 표준이 패딩을 허용하더라도). 배열은 또한 같은 주소에서 시작하여 구조물에서 '패딩 없음'이 적용되며 요소는 두 구조물과 겹칩니다. 나는 문제가있을 것이라는 것을 정말로 보지 못한다.

다른 사람들이 언급했듯이 노조로 이것을 얻을 수 있습니다. 색상과 동일한 구조물에 과부하 색상 및 위치에 위치하면 좋은 생각이 아닐 수 있습니다 (예 : 두 가지 색상을 추가하면 일반적으로 1.0으로 포화되고 싶지만 벡터를 추가하면 선형 적으로 발생합니다). 그것은 완벽하게 훌륭하고 데이터를 GL/DirectX/등과 교환하는 잘 받아 들여진 수단입니다.

그러나 동일한 함수 범위에서 다른 별칭으로 동일한 멤버를 언급하지 않는 것이 좋습니다. 그러나로드 하이트 스토어라고 불리는 불쾌한 하드웨어 스톨으로 이끌어주기 때문입니다. 특히, 가능하다면 이것을 피하십시오.

vector foo; 
foo.x = 1.0f;
return foo[0] + foo[1];

참조?

template<typename T>
struct vertex {
    vertex() :
        r(data[0]), g(data[1]), b(data[2]),
        x(data[0]), y(data[1]), z(data[2])
    {
    }

    T *operator *() {
        return data;
    }

    const T *operator *() const {
        return data;
    }

    T data[3];
    T &r, &g, &b;
    T &x, &y, &z;
};

나는 당신이 원하는 것을 얻기 위해 매크로 마법을 할 수 있다고 생각합니다. 그러나 그것은 추악 해 보일 것입니다. 왜 3 가지 다른 유형에 동일한 구조물, 정점을 사용하고 싶습니까? 색상에 대한 클래스를 정의 할 수없는 이유는 무엇입니까? 또한 정점과 색상은 동일하지 않다는 점을 명심하십시오. Vertex로 무언가를 변경하면 두 가지에 대해 동일한 클래스가있는 경우 색상에도 영향을 미칩니다.

질문을 올바르게 이해했는지 확실하지 않습니다. 그러나 구조물/클래스에 대한 액세스와 같은 배열을 제공하려면 연산자 []에 과부하가 필요한 것 같습니다. 여기에 언급 된 예를 참조하십시오. 연산자 과부하

다음 구조에는 요청 된 동작이 있습니다.

struct vertex
{
private:
    float data[3];
public:
    float &x, &y, &z;
    float &r, &g, &b;

    vertex() : x(data[0]), y(data[1]), z(data[2]), r(data[0]), g(data[1]), b(data[2]) {
    }

    float& operator [](int i) { 
        return data[i];
    }
};

내 생각에 나쁜 생각은 적어도 주어진 예에 대해 : 단점은 이것에 대한 해결책에 대해 "xyz"인스턴스에 "rgb"인스턴스를 자유롭게 할당 할 수 있다는 것입니다. 아마도 합리적이거나 정확하지 않을 것입니다. 즉 유용한 유형의 안전을 포기할 위험이 있습니다.

개인적으로, 당신이주는 예를 위해, 나는 기본에서 RGB와 XYZ 유형을 서브 클래스 boost::array<float,3> 또는 유사합니다. 따라서 둘 다 연산자 []를 상속 받고 배열을 기대하는 함수로 전달 될 수 있으며 색상/좌표를 기대하는 것들에 더 많은 유형의 안전성을 전달할 수 있습니다. 종종 XYZ 또는 RGB를 배열로 취급하고 싶지만 XYZ를 RGB로 취급하거나 그 반대로 처리하려는 드물게 드물게됩니다. (RGB IS-A 배열 : OK. XYZ IS-A ARRAY : OK. RGB IS A XYZ ???? 나는 그렇게 생각하지 않습니다!)

물론 그것은 X, y, Z & R, G, B에 대한 액세스를 의미합니다. operator[](...) ) 회원에게 지시하기보다는. (C#의 속성이 필요합니다).

아래에는 템플릿과 두 개의 벡터 클래스가 있습니다. 하나는 미친, 하나는 제정신입니다. 템플릿은 컴파일 타임 배열에서 단순한 고정 값을 구현합니다. 서브 클래싱 용으로 설계되었으며 보호 된 배열 변수를 사용하여 배열에 액세스하기 위해 후프를 뛰어 넘지 않아도됩니다. (일부 사람들은 그러한 디자인을 좋아하지 않을 수도 있습니다. 나는 당신의 서브 클래스가 과부하 된 연산자에게 전화를 걸면 커플 링이 좋은 생각 일 수 있습니다.)

Crazy Class를 사용하면 X, Y, Z라는 멤버 변수를 가질 수 있으며 GlgetFloatV에 대한 호출 배열처럼 작용합니다. Sane One은 액세서 기능 x (), y (), z ()를 가지고 있으며 여전히 Glgetfloatv와 함께 작동합니다. OpenGL 라이브러리로 전달할 수있는 다른 벡터 객체의 기초로 클래스를 사용할 수 있습니다. 아래 클래스는 포인트에 따라 다르지만 검색/교체를 수행하여 RGB 색상 클래스로 전환 할 수 있습니다.

vec.x () 대신 구문 설탕 vec.x의 비용은 3 참조 변수이기 때문에 미친 클래스는 미쳤다. 큰 응용 프로그램에서 많은 공간을 차지할 수 있습니다. 간단한 제정신 버전을 사용하십시오.

template <typename T, int N>
class FixedVector {
protected:
    T arr[N];
public:
    FixedVector();

    FixedVector(const T* a) {
        for (int i = 0; i < N; ++i) {
            arr[i] = a[i];
        }
    }

    FixedVector(const T& other) {
        for (int i = 0; i < N; ++i) {
            arr[i] = other.arr[i];
        }
    }

    FixedVector& operator=(const T& other) {
        for (int i = 0; i < N; ++i) {
            arr[i] = other.arr[i];
        }
        return *this;
    }

    T* operator&() { return arr; }
    const T* operator&() const { return arr; }

    T& operator[](int ofs) { 
        assert(ofs >= 0 && ofs < N);
        return arr[ofs];
    }
    const T& operator[](int ofs) const { 
        assert(ofs >= 0 && ofs < N);
        return arr[ofs];
    }
};

class CrazyPoint :  public FixedVector<float, 3> {
public:
    float &x, &y, &z;

    CrazyPoint()
      : x(arr[0]), y(arr[1]), z(arr[2])
    { arr[0] = arr[1] = arr[2] = 0.0; }

    CrazyPoint(const float* a)
      : x(arr[0]), y(arr[1]), z(arr[2])
    {
        arr[0] = a[0];
        arr[1] = a[1];
        arr[2] = a[2];
    }

    CrazyPoint(float a, float b, float c) 
      : x(a), y(b), z(c)
    {
        arr[0] = a;
        arr[1] = b;
        arr[2] = c;
    }
};

class SanePoint : public FixedVector<float, 3> {
public:
    float& x() { return arr[0]; }
    float& y() { return arr[1]; }
    float& z() { return arr[2]; }

    SanePoint() { arr[0] = arr[1] = arr[2] = 0.0; }
    SanePoint(float a, float b, float c) 
    {
        arr[0] = a;
        arr[1] = b;
        arr[2] = c;
    }
};

// usage
SanePoint normal;
glGetFloatV(GL_CURRENT_NORMAL, &normal);

다음과 같은 변수에 대한 참조를 추가 할 수 있습니다.

struct test {
        float x, y, z;
        float &r, &g, &b;

        test() : r(x), g(y), b(z) {}
    };

그러나 구조가 커집니다 (12 바이트에서 40 바이트).

]를 사용하려면 이전에 언급했듯이 연산자 []의 과부하를 사용하십시오.

멤버를 가리키는 참조 회원을 사용하는 것에 대한 경고 만 있습니다. 해당 객체를 복사 한 경우 (값으로 전송) 복사 한 경우 사본 생성자 (및 할당 연산자)를 정의해야합니다. 기본 사본 생성자는 새 객체의 객체가 아닌 원래 오브젝트의 값 멤버를 가리키는 사본을 남겨 둡니다. 이것은 확실히 당신이 원하는 것이 아닙니다.

이미 지적했듯이 더 큰 물체로 끝나는 것을 고려할 때, 액세서 방법을 사용하는 것이 참조 멤버보다 선호되어야한다고 생각합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top