【问题标题】:Is there a way to call member's getter/setter in C++ without parentheses?有没有办法在没有括号的情况下在 C++ 中调用成员的 getter/setter?
【发布时间】:2019-08-04 16:42:51
【问题描述】:

我想通过#including 在 C++ 应用程序中运行一些 GLSL 代码作为 C++ 代码,无需修改。它没有特殊的编译指示等,但它有一些向量类型。例如vec3 和vec4,分别表示 3 和 4 个浮点数的元组,即内存中的 contig,方便一些线性代数任务。

除了一件事之外,几乎所有事情都可以通过运算符重载来完成。 考虑vec4 a 有4 个浮动成员:a.x、a.y、a.z 和a.w;

在 glsl 中,有一个流行的符号 a.xyz,意思是“取 a.x,a.y,a.z 并从中构造 vec3。”

一些语言,比如 Pascal(Delphi) 的面向对象版本有这样的功能:这些成员是用 property 关键字定义的。因此,当编译器遇到它时,它会调用 getter,当它遇到赋值时 - 它会调用 setter,一切看起来就像您正在读取或写入常规成员变量。

我们如何在 C++ 中这样写?


class vec4 {
public:
    float x,y,z,w;
    property xyz read=get_xyz write=set_xyz; // ??? is there some way?
protected:
    vec3 get_xyz() const { return vec3(x, y, z); }
    void set_xyz(const vec3& v) { x = v.x; y=v.y; z=v.z; }
};

在代码中,我想这样写getter:

vec4 a(1,2,3,4);
auto b = a.xyz; // b = a.get_xyz();

b 现在是 vec3 类型,具有 x==1、y==2、z==3;

然后像这样设置:

vec4 a(1,2,3,4);
a.xyz=vec3(5,6,7); // a.set_xyz(vec3(5,6,7));

a 现在有x==5、y==6、z==7 和w==1;


我怎样才能支持这样的语法?

【问题讨论】:

  • 我从来没有见过用 C++ 完成的。在设置器的情况下,我想可以为该类型重载 operator= 但是......我几乎不认为这是一个好主意。
  • 似乎您正在寻找 c# 或 delphi 等语言支持的 property 语法。 C++ 不支持。
  • 您可以在 C++ 中执行类似 C# 属性的操作。 Michael Litvin 在他的template to mimic properties 回答中展示了一个示例。但是,我不建议您这样做。它不是惯用的 C++。
  • 你不能仅仅在 C++ 中发明新语法,不。
  • @πάνταῥεῖ 刚问过,也许是新标准中的新功能。顺便说一句,您可能不会将 C# 与 Delphi 进行比较。因为 C# 起源于解释器,(更像是 msdos 的 qbasic 模拟器玩具)。 delphi 是一个 pascal 扩展(turbo/borland pascal,最初直接编译为 16 位 asm),更像 C/C++ - 支持那些低级功能,如使用指针、位操作等。 Delphi 只是支持属性的最接近的“普通”语言(不是 java/c#/..)。我看不出任何理由,对 C++19 仍然缺乏这样的语法糖感到失望......

标签: c++ oop operator-overloading overloading getter-setter


【解决方案1】:

一个可悲的事实是,如此成熟、强大和广泛使用的 C++ 语言没有 property 支持。甚至 Delphi 也有.. 幸运的是,我设法解决了任务(在不修改 GLSL 源代码的情况下将计算 glsl 着色器编译为 C++)。我认为它不会有与创建代理对象相关的速度/内存损失,并且可以很容易地被编译器优化(尽管这不是必需的)。

我找到了以下解决方案:

Try it online!

#include <iostream>

#pragma pack(push, 4)
class vec3 {
public:
    float x,y,z;
    vec3() {}
    vec3(float x, float y, float z) :
        x(x), y(y), z(z)
    {}
};

class vec4 {
public:
    union {
        struct {
            float x, y, z, w;
        };
        vec3 xyz;
    };
    vec4() {}
    vec4(float x, float y, float z, float w):
        x(x), y(y), z(z), w(w)
    {}
};

#pragma pack(pop)

int main() {
    {
        vec4 a(1, 2, 3, 4);
        auto b = a.xyz; // b = a.get_xyz();
        std::cout << b.x << b.y << b.z << std::endl;
    }
    {
        vec4 a(1, 2, 3, 4);
        a.xyz = vec3(5, 6, 7); // a.set_xyz(vec3(5,6,7));
        std::cout << a.x << a.y << a.z << a.w << std::endl;
    }
    return 0;
}

【讨论】:

  • 请注意,C++ 不允许(未定义的行为)写入一个联合成员并从另一个联合成员读取。
  • @Eljay 感谢您的重要通知。如果您手头有一个,请随意发布另一种可能的解决方案。只需复制我的 main() 函数 - 它必须无需修改即可工作,产生相同的输出(与 TIO 的 gcc 一样)。速度和内存使用无关紧要(这是理论上的练习)。
  • 在罗马时...我会使用带括号的 getter 和 setter 方法,因为这种 C++ 不会混淆任何其他 C++ 开发人员。我会使用vec3 xyz() const 作为吸气剂,并同时提供xyz(vec3) 和xyz(float x, float y, float z) 作为设置器。虽然我的 druthers 是让 vec3 和 vec4 都是不可变的,而 setter 将是返回新 vec3 或 vec4 的工厂——但这是一个完整的“另一个话题”。
  • @Eljay 现在想象 window.title.. 你一定觉得它是一个字符串。 window.title="Hello"; 对 C++ 来说似乎是最明显的 .. 哦,对不起,即使对非程序员来说也是如此......在引擎盖下,在 win32 api 上它甚至不是一个变量!你必须打电话给SetWindowTextA(hWnd, newWindowTitle.c_str())..在吸气剂方面更糟:你想像std::string s(256, '\0'); auto newLen=GetWindowTextA(hWnd, &amp;s[0], s.size()); s.resize(newLen); return s;一样去所有这些可能并且应该暴露为title你漂亮的window 类的财产..
  • @Eljay 这是一个激励的例子。根据任务,您的变体不起作用。它在auto b = a.xyz 语句上给出编译错误,内容为: error C3867: 'vec4::xyz': non-standard syntax;使用 '&' 创建指向成员的指针 所以我不能接受它作为解决方案。
猜你喜欢
  • 2017-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-05
  • 2018-11-05
  • 2012-06-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多