Что нарушает этот шаблон C ++ setter / getter?

Используя синтаксис GLSL в C ++

, я написал собственные векторные классы, такие как vec2 , vec3 и т.д., которые имитируют типы GLSL и выглядят примерно так:

struct vec3
{
    inline vec3(float x, float y, float z)
      : x(x), y(y), z(z) {}
    union { float x, r, s; };
    union { float y, g, t; };
    union { float z, b, p; };
};

Операции с векторами реализуются следующим образом:

inline vec3 operator +(vec3 a, vec3 b)
{
    return vec3(a.x + b.x, a.y + b.y, a.z + b.z);
}

Это позволяет мне создавать векторы и получать доступ к их компонентам, используя синтаксис, подобный GLSL, и выполнять операции над ними почти так же, как если бы они были числовыми типами. Объединения позволяют мне безразлично ссылаться на первую координату как x или как r , как в случае с GLSL. Например:

vec3 point = vec3(1.f, 2.f, 3.f);
vec3 other = point + point;
point.x = other.b;

Проблема swizzling

Но GLSL также допускает swizzling доступ, даже с дырами между компонентами. Например, p.yx ведет себя как vec2 с замененными местами p s x и y . Если ни один компонент не повторяется, это также lvalue. Некоторые примеры:

other = point.xyy; /* Note: xyy, not xyz */
other.xz = point.xz;
point.xy = other.xx + vec2(1.0f, 2.0f);

Теперь это можно сделать с помощью стандартных геттеров и сеттеров, таких как vec2 xy () и void xy (vec2 val) . Это то, что делает библиотека GLM .

Прозрачный метод получения и установки

Однако я разработал этот шаблон, который позволяет мне делать то же самое в C ++. Поскольку все является POD-структурой, я могу добавить больше объединений:

template struct MagicVec2
{
    friend struct vec2;
    inline vec2 operator =(vec2 that);

private:
    float ptr[1 + (I > J ? I : J)];
};

template
inline vec2 MagicVec2::operator =(vec2 that)
{
    ptr[I] = that.x; ptr[J] = that.y;
    return *this;
}

и например. класс vec3 становится (я немного упростил, например, ничто не мешает использовать xx здесь как lvalue):

struct vec3
{
    inline vec3(float x, float y, float z)
      : x(x), y(y), z(z) {}

    template
    inline vec3(MagicVec3 const &v)
      : x(v.ptr[I]), y(v.ptr[J]), z(v.ptr[K]) {}

    union
    {
        struct { float x, y, z; };
        struct { float r, g, b; };
        struct { float s, t, p; };

        MagicVec2<0,0> xx, rr, ss;
        MagicVec2<0,1> xy, rg, st;
        MagicVec2<0,2> xz, rb, sp;
        MagicVec2<1,0> yx, gr, ts;
        MagicVec2<1,1> yy, gg, tt;
        MagicVec2<1,2> yz, gb, tp;
        MagicVec2<2,0> zx, br, ps;
        MagicVec2<2,1> zy, bg, pt;
        MagicVec2<2,2> zz, bb, pp;
        /* Also MagicVec3 and MagicVec4, of course */
    };
};

В основном: я использую объединение для смешайте компоненты вектора с плавающей запятой с магическим объектом, который на самом деле не является vec2 , но может быть неявно преобразован в vec2 (потому что существует конструктор vec2 , позволяющий it), и ему может быть присвоено значение vec2 (из-за его перегруженного оператора присваивания).

Результатом очень доволен. Приведенный выше код GLSL работает, и я считаю, что получаю приличную безопасность типов. И я могу # включить шейдер GLSL в свой код C ++.

Ограничения

Конечно, есть ограничения. Я знаю следующие:

  • sizeof (point.xz) будет 3 * sizeof (float) вместо ожидаемого 2 * sizeof (float) . Это сделано намеренно, и я не знаю, может ли это быть проблемой.
  • & foo.xz не может использоваться как vec2 * . Это должно быть нормально, потому что я всегда передаю эти объекты только по значению.

Итак, мой вопрос: что я мог упустить из виду, что усложнит мою жизнь с этим шаблоном? Кроме того, я еще нигде не нашел этот шаблон, поэтому, если кто-то знает его название, мне интересно .

Примечание: я хочу придерживаться C ++ 98, но я полагаюсь на компилятор, позволяющий набирать типы с помощью объединений.Моя причина, по которой я пока не хочу C ++ 11, - это отсутствие поддержки компилятора на некоторых из моих целевых платформ; все же интересующие меня компиляторы поддерживают каламбур.

5
задан sam hocevar 26 January 2012 в 01:34
поделиться