【问题标题】:Create macro to generate getters for any class c++创建宏来为任何类 c++ 生成 getter
【发布时间】:2022-01-25 06:13:24
【问题描述】:

最近我开始思考如何通过名称的通用类/函数来通用对私有数据成员的访问。原因是我有很多私人成员,为每个成员创建一个吸气剂很麻烦。我尝试使用预处理器宏来执行以下操作

#define RETURNS(...) -> decltype((__VA_ARGS__))  { return (__VA_ARGS__); }
#define GET(classname, name, funcname) auto funcname() RETURNS(classname->name);

class foo {
private: 
    int a = 1;
    std::vector<int> b = std::vector<int>(3, 1);
    std::string c = "pika-chuuu";
public:
    foo() = default;
    ~foo() = default;

    GET(this, a, getter);
};

int main(const int argc, char* argv[]) {

    foo obj;
    std::cout << obj.getter();
    return 0;
}

这个可以编译,但是有没有办法可以在foo 中创建一个getter,它在运行时获取一个变量的名称并返回this-&gt;(name)?使用这种方法,我可以减少引入 getter 的代码,但是数据成员越多,我需要的 getter 就越多,但我希望有一个可以按名称访问任何数据成员的权限。你有什么建议可以做到吗? 我正在寻找这样的语法:

#define RETURNS(...) -> decltype((__VA_ARGS__))  { return (__VA_ARGS__); }
#define GET(classname, name) RETURNS(classname->name);
class foo {
private: 
    int a = 1;
    std::vector<int> b = std::vector<int>(3, 1);
    std::string c = "pika-chuuu";
public:
    foo() = default;
    ~foo() = default;

    auto getter(auto x) GET(this, x);
};

这里 x 是我输入的名称,a,bc

【问题讨论】:

  • 对 getter 的严重依赖通常表明设计缺乏抽象。您是否尝试过从另一个角度解决问题,质疑为什么需要这么多?
  • @molbdnilo 在我的设计中,a 有很多成员,用户本身应该无法访问,所以我宁愿省略让它们公开。我使用的 getter 返回值或对 const 对象的引用,因此用户无法更改它们,但他可能需要对特定字段的副本或引用。一种方法是将所有这些参数打包到一个结构中并为该结构创建一个 getter,但这在实现中可能会变得很麻烦,将 this->var 重写为 this->structname.var 等。
  • 拥有大量成员再次表明设计存在缺陷。为什么其他参与者需要访问他们中的所有?也许某些成员应该是其他类的成员?而且,使用宏几乎没有是解决设计问题的方法。更频繁地简化类是,
  • @BoP “也许有些成员应该是其他类的成员?”你的意思是定义一个结构来保存我需要getter的那些数据成员?如果可以更改它,我将尝试查看设计,将成员分离到其他类(3 个类继承自具有这些成员和 getter 的基类)。无论如何,有没有办法定义这样的函数?
  • 你的意思是定义一个结构来保存我需要 getter 的那些数据成员? 不,我的意思是如果其他一些类经常需要获取一些值,也许那些相反,值属于该其他类。有一些 getter 是可以的,需要 许多 getter 是可疑的。

标签: c++ getter preprocessor


【解决方案1】:

你有什么建议吗?

为什么所有decltype-&gt; 以及带有__VA_ARGS__RETURNS 的可变参数宏和...只是:

#include <vector>
#include <string>

#define DECL_GETTER(name) \
        auto get_##name() { return this->name; }

class foo {
private: 
    int a = 1;
    std::vector<int> b = std::vector<int>(3, 1);
    std::string c = "pika-chuuu";
public:
    DECL_GETTER(a)
    DECL_GETTER(b)
    DECL_GETTER(c)
};

int main() {
    foo f;
    f.get_a();
    f.get_b();
    f.get_c();
}

我还会添加const重载。

你可能想研究一下QT properties system,这基本上就像一个更高级的版本。

主观:TBH 使用这样一个宏,您在不需要它们的地方进行抽象。值得花时间吗?它用不明显的宏隐藏了一些明显的代码,使 IDE 更难“跳转到定义”,使维护和推理更难。使用 IDE 功能重命名变量会更难。考虑只逐字逐句地编写这些 getter - 考虑一目了然的可读且清晰的代码,即使您必须牺牲一点重复并输入 () { return 更多字符。配置您的 IDE,这样您就可以使用“为此成员函数创建一个 getter/setter”操作来加快您的开发速度(参见 QT Creator,这是一个非常棒的功能)。

有没有办法在 foo 中创建一个 getter,它在运行时获取变量的名称并返回 this->(name)?

C++ 是一种没有reflection 的语言,所以基本上没有,否则它需要的样板代码比它的价值要多得多。

【讨论】:

  • RETURNS 宏在代码的另一个位置使用,并且 decltype(...) 是使用 C++11/14 编译器的残余。我将研究 QT 属性,但如果无法声明这样的宏/函数,那么我将坚持使用我现在使用的 mu gettter
  • 无论如何,非常感谢!我看到在这种情况下我也不需要在宏中传递类,因为无论如何我都不能在类之外声明 getter
【解决方案2】:

修订:

正如@HolyBlackCat所说,不需要堆分配,你应该直接使用impl类作为对象:

class foo{
    struct foo_data
    {
        int i;
        std::string s;
    };
    foo_data data;
public:
    template<typename ... Args>
    foo(Args&& ... args)
    : data(std::forward<Args>(args)...)
    {}

    foo_data const* operator->() const   // Returns a pointer to const data
    {
        return &data;
    }
};

然后访问数据:

int main()
{
    auto f = foo(3, "hello");
    std::cout << f->i << f->s;          // Accessing data, prints: "3hello"
    // f->i += 1;                       // Error: assignment of member in read-only object
}

【讨论】:

  • 这实际上是一个有趣的解决方案。毕竟这可能会有所帮助,谢谢!
  • 对于 foo 的成员函数,如果我写 this->i ,编译器会“知道”吗?那个时候操作符会超载吗?还是该运算符只能在类实例的类范围之外工作,其中将指定输入?
  • @Qant123 我已经更新了实现,您可能想要使用新的。是的,您可以使用类中的任何成员函数,包括重载operator-&gt;。但是,正确的语法应该是 (*this)-&gt;i
  • @Qant123 还要注意重载的operator-&gt; 的工作是返回一个不可变对象。由于不需要限制类内的可变性,您应该直接使用data.i 中的foo 访问数据。
  • 感谢您的帮助和代码!现在我坚持使用简单的 getter(如果我发现它们没有必要,甚至可以删除它们)以省略使用 this->data.something 重写对数据成员的每个调用。但这有时肯定会派上用场,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多