2

Lot's of year from last project in C++, I cannot remember/find how to do this.

Sample (pseudo code) :

    MyClass
    {
    public :
        float x;
        float y;
        float z;
    }

    main.cpp

    void MyFunction(void)
    {
        MyClass *myclass = new MyClass(); 
        float x = myclass->x;

        //want I want :
        float *xyz = myclass->xyz;
    }

How to do this ?

Thank you very much and sorry for my poor english.

[EDITED] It's only a concept now, but the goal, is near the vec4 class in GLSL (OpenGL Shader for GPU). Vec4 is a math vector class with four values (x, y, z, w). You can get/assign value like this sample :

vec4 vectorA = vec4(1.0, 1.0, 1.0, 1.0);
vectorA.x = 2.0;
vec2 vectorB = vectorA.xy;
vec3 vectorC = vectorA.xxx;

etc. (so : VectorC.x = 2.0, vectorC.y = 2.0, vectorC.z = 2.0)

4

3 回答 3

8

使用未命名的结构

union Vector
{
    struct 
    {
        float x;
        float y;
        float z;
    };
    float xyz[3];
};

然后您可以访问组件而无需隐式引用包含结构:

int main()
{ 
    Vector* vec = new Vector();
    vec->x = 50;
    vec->y = 30;
    vec->xyz[2] = vec->xyz[0] + vec->xyz[1]; // vec->z == 80
    delete vec;
    return 0;
}

当然,你可以用另一个结构/类包装这个联合,达到同样的效果:

class MyClass
{
public:
    union
    {
        struct 
        {
            float x;
            float y;
            float z;
        };
        float xyz[3];
    };
};

另外,为什么要在堆上创建结构(使用“new”)?不会在堆栈上分配吗?

编辑:哦,我明白了。好吧,这绝对是可行的,但只有在您希望尽可能多地与 GLSL 兼容时才值得。这个想法是创建一个“代理”来存储每个组件变体的引用。权衡是 vec2,而不是占用 8 个字节的内存将占用 40 个字节。对于 vec3 和 vec4 显然会变得更糟

class vec2
{
    // private proxy, auto-convertible into vec2
    struct proxy2
    {
        // store references, not values!
        proxy2(float &x, float &y) : x(x), y(y) {}
        // implicit conversion to vec2
        operator vec2() { return vec2(x, y); }
        // support assignments from vec2
        proxy2& operator=(const vec2& vec) 
        { 
            x = vec.x; 
            y = vec.y; 
            return *this; 
        }
    private:
        // hide copy and assignment operators
        proxy2(const proxy2&);
        proxy2& operator=(const proxy2&);
        // hide member variables
        float& x;
        float& y;
    };

public:
    vec2(float _x, float _y) 
        : x(_x), y(_y) 
        , xx(x, x), xy(x, y), yx(y, x), yy(y, y)
    {}

    vec2(const vec2& vec)
        : x(vec.x), y(vec.y)
        , xx(x, x), xy(x, y), yx(y, x) , yy(y, y)
    {}

    float x;
    float y;
    proxy2 xx;
    proxy2 xy;
    proxy2 yx;
    proxy2 yy;
};

通过这个类,您可以获得非常接近 GLSL 提供的语法:

vec2 v(1.0f, 2.0f);
vec2 vxx = v.xx; // 1, 1
vec2 vyx = v.yx; // 2, 1
vec2 vxy = v.xy; // 1, 2
vec2 vyy = v.yy; // 2, 2

v.yx = vec2(3, 4); // 4, 3
v.y = 5;           // 4, 5

vec2::proxy2 proxy = v.xx;     // compile error
v.xx = vec2::proxy2(v.x, v.y); // compile error

要将其扩展为支持vec3vec4简单地从vec2和派生vec3,请为每个组件变体创建proxy3proxy4构造并声明成员(27vec3个,仅 64 个vec4)。

EDIT2:新版本,根本不占用额外空间。再次,工会来救援!转换proxy2为模板并添加与vec2组件​​匹配的数据成员,您可以安全地将其放入联合中。

class vec2
{
    // private proxy, auto-convertible into vec2
    template <int x, int y>
    struct proxy2
    {
        // implicit conversion to vec2
        operator vec2()
        { 
            return vec2(arr[x], arr[y]); 
        }
        // support assignments from vec2
        proxy2& operator=(const vec2& vec) 
        { 
            arr[x] = vec.x; 
            arr[y] = vec.y; 
            return *this; 
        }
    private:
        float arr[2];
    };

public:
    vec2(float _x, float _y) 
        : x(_x), y(_y) 
    {}

    vec2(const vec2& vec)
        : x(vec.x), y(vec.y)
    {}

    union
    {
        struct 
        {
            float x;
            float y;
        };
        proxy2<0, 0> xx;
        proxy2<0, 1> xy;
        proxy2<1, 0> yx;
        proxy2<1, 1> yy;
    };
};

希望这就是你所追求的。

EDIT3:我花了一段时间,但我想出了一个有效的 GLSL 仿真库(包括 swizzling),让您无需修改​​即可运行片段着色器。如果你仍然感兴趣,你应该看看。

于 2012-10-23T08:24:09.183 回答
3

C++ 可以容纳类似vec.xyx的语法,但编写起来并不容易。而且您不会通过一项一项添加功能来实现这一目标。最好列出需求,选择工具,直截了当。

你需要什么:

  1. 像这样的存储类std::array
  2. 成员名为x, y, ... xy, xz, ... xyz, xzx, ...</li>
  3. 将这些成员转换为所需输出的东西
  4. 为输出提供所需语义的类型

第一个要求很简单:使用std::array.

接下来,您必须定义 3 + 3^2 + 3^3 = 39 个成员。这可以通过复制粘贴来完成,但最好使用模板元编程。有z会员是必须的。

成员的类型没有意义,但必须告诉编译器如何从数组中选择命名元素。

例子:

selection_vector< 0, 1, 0 > xyx;
selection_vector< 0, 1, 1 > xyy; // ad nauseam

理想情况下,这些成员会知道如何选择没有状态的元素,但它们需要被初始化this并占用一个指针。因此请注意,每个 3 向量对象会浪费 312 个字节。

要让成员做某事,你必须定义转换函数。所以你有类似的东西

selection_vector::operator array3_type() { return { ptr[0], ptr[1], ptr[2] }; }

隐式转换函数在执行赋值和作为函数参数传递时适用this,但在许多其他情况下不适用。所以要获取vec.xyx.x或者类型就需要定义额外vec.xyx[ 1 ]的成员。selection_vector

一旦你定义了疯狂类型和运算符重载的网络,你就可以节省一些击键......</p>

轻微妥协

听起来您并不想妥协,但->*运营商值得一提。这是实现下标的最佳非成员运算符重载。

这允许像

xyx_type xyx;

template< typename vec >
my_3vector< vec > operator->* ( vec &&v, xyx_type )
    { return { v[0], v[1], v[2] }; }

std::array< float, 3 > a { 0.5, 1.5, 9 };

my_3vector< … > b = a->*xyx;

您甚至可以my_3vector简单地制作std::array并避免任何模板元编程。xyx_type进行枚举以避免预处理器元编程。

操作员->*代表。.这让事情变得容易多了,但请注意,这->*有一个有趣的优先级;它低于.并且->您期望它是对等的。

于 2012-10-23T08:38:19.453 回答
2

这是另一种可能的解决方案,与@gwiazdorrr 发布的基于联合的示例略有不同。它假设

#include <cassert>
#include <algorithm>
#include <stdexcept>

struct MyClass
{
    enum { size = 3 };

    typedef float& reference;
    reference x;
    reference y;
    reference z;

    MyClass() 
        : x(xyz[0] = 0), y(xyz[1] = 0), z(xyz[2] = 0)
    {}
    MyClass(float x, float y, float z)
        : x(xyz[0] = x), y(xyz[1] = y), z(xyz[2] = z)
    {}
    MyClass& operator=(MyClass const& other)
    {
        std::copy(other.xyz, other.xyz + size, xyz);
        return *this;
    }

    // convenient indexed access
    reference operator[](std::size_t index)
    {
        if (index < size)
            return xyz[index];
        else
            throw std::out_of_range("index not less than size");
    }

    // raw data access
    float* data() { return xyz; }

private:
    float xyz[size];
};

int main()
{
    MyClass c1;
    MyClass c2(1, 2, 3);
    c1 = c2;
    assert(c1.data()[0] == c2[0]);
    assert(c1.data()[1] == c2[1]);
    assert(c1.data()[2] == c2[2]);

    MyClass c3(c2);
    assert(c2[0] == c3.x);
    assert(c2[1] == c3.y);
    assert(c2[2] == c3.z);
}

我假设无法访问 C++11,因此构造函数中的初始化体操。

于 2012-12-12T17:12:24.923 回答