【问题标题】:Is there a Perl script to implement C++ Class get/set member functions?是否有实现 C++ 类获取/设置成员函数的 Perl 脚本?
【发布时间】:2010-10-23 15:55:10
【问题描述】:

今天早上我正在阅读 The Pragmatic Programmer 第 3 章关于每个程序员都应该拥有的基本工具的书,其中提到了代码生成工具。 他们提到了一个用于 C++ 程序的 Perl 脚本,该脚本有助于自动化实现私有数据成员的 get/set() 成员函数的过程。

有谁知道这样的脚本以及在哪里可以找到它?我一直无法想出正确的谷歌关键字来找到它。

【问题讨论】:

    标签: c++ perl code-generation


    【解决方案1】:

    虽然它没有直接回答您的问题,但您可能会发现生成的代码实际上对于管理 C++ 中的属性是不必要的。下面的模板代码可以让你方便的声明和使用属性:

    // Declare your class containing a few properties
    class my_class {
    public:
        property<int> x;
        property<string> y;
        ...
    };
    
    ...
    
    my_class obj;
    cout << obj.x();          // Get
    obj.y("Hello, world!");   // Set
    

    代码如下:

    // Utility template to choose the 2nd type if the 1st is void
    template <typename T, typename U>
    struct replace_void {
        typedef T type;
    };
    
    template <typename T>
    struct replace_void<void, T> {
        typedef T type;
    };
    
    // Getter/setter template
    template <typename T, typename D = void>
    class property {
        typedef typename replace_void<D, property>::type derived_type;
    
        derived_type& derived() { return static_cast<derived_type&>(*this); }
    
    public:
        property() {}   // May be safer to omit the default ctor
        explicit property(T const& v) : _v(v) {}
        property(property const& p) : _v(p._v) {}
        property& operator=(property const& p) { _v = p._v; return *this; }
    
        T operator()() const { return _v; }                 // Getter
        void operator()(T const& v) { derived().check(v); _v = v; }   // Setter
    
    protected:
        // Default no-op check (derive to override)
        void check(T const& v) const { (void)v; //avoid unused variable warning}
    
    private:
        T _v;
    };
    

    check() 是一个测试分配的值是否有效的函数。您可以在子类中覆盖它:

    class nonnegative_int : public property<int, nonnegative_int> {
    public:
        // Have to redeclare all relevant ctors unfortunately :(
        nonnegative_int(int v) : property<int, nonnegative_int>(v) {}
    
        void check(int const& v) const {
            if (v < 0) {
                throw "Yikes! A negative integer!";
            }
        }
    };
    

    你有它 - 外部生成的 getter/setter 函数的所有优点,没有任何混乱! :)

    您可以选择让check() 返回一个bool 来指示有效性,而不是抛出异常。原则上,您可以添加一个类似的方法 access() 来捕获对该属性的读取引用。

    编辑: 正如 Fooz 先生在 cmets 中指出的那样,类作者稍后可以在不修改类的逻辑结构的情况下更改实现(例如,将 property&lt;int&gt; x 成员替换为一对x() 方法),尽管丢失了二进制兼容性,因此无论何时进行此类更改,用户都需要重新编译其客户端代码。 这种轻松整合未来变化的能力实际上是人们首先使用 getter/setter 函数而不是公共成员的主要原因。

    性能说明:因为我们使用CRTP 来实现“编译时多态性”,所以在子类中提供您自己的check() 没有虚拟调用开销,并且您不需要声明它virtual

    【讨论】:

    • 哇!我实际上想过使用模板来模拟属性并回来编辑我的答案......只是在这里找到了这个解决方案,它比我要发布的简单黑客更完整!感谢分享。
    • 可能值得注意的是,类作者可以在不修改类逻辑结构的情况下更改实现(例如,直接制作一对x()方法或更改D实现),但二进制兼容性丢失了,因此每当进行此类更改时,用户都需要重新编译其客户端代码。
    • @Mr Fooz:很好,这实际上是人们首先使用 getter/setter 的原因!我已经更新了文章。 (小问题:我不认为 D 可以更改,因为 derived() 中的 static_cast 将在编译时失败,除非 D 实际上是 *this 的子类。)
    • @nmuntz:我建议您可以使用公共属性 成员而不是 私有 foo 成员加上公共 getter 和 setter 方法的通常组合。 原因 人们完全使用 getter/setter(而不仅仅是普通的公共成员 foo)是为了(a)可以检查新分配的值的有效性,(b)可以维护内部关系(例如,分配给 square.width 应该更新 square.area)和(c)未来的实现可以改变值的存储方式(例如在数据库中),而无需重写客户端代码...
    • ... Getter/setter 通过让您手动编写自己的 getter/setter 函数来处理 (b) 和 (c)(如果您现在从数据库中获取数据,则可能删除私有成员) 而不是使用自动生成的版本。您可以对 property 模板执行完全相同的操作,also 允许 (a) 以最小的努力(只需子类化并提供您自己的 check())。正如 Fooz 先生所说,您可以在不破坏客户端代码的情况下切换回“传统”getter/setter 样式。
    【解决方案2】:

    由于大多数 C++ 私有成员不应通过 Get/Set 样式函数访问,这似乎是个坏主意。

    关于为什么会这样的简单示例,请考虑 C++ std::string 类。它的私有成员可能看起来像这样(确切的实现并不重要):

    private:
       int last, len;
       char * data;
    

    您认为为它们提供 get/set 成员是否有意义?

    【讨论】:

    • 我认为您没有理解这个问题。这些变量是私有的,因此类可以控制它们的更改方式并有机会对任何更改做出反应,但这些变量的公共接口由 get/set 方法实现。在像 C++ 这样的语言中,无法在不更改公共接口的情况下追溯添加对成员变异的控制,这根本不是一个坏主意。
    • 我明白你的意思。并非所有私有数据成员都应该有访问器和/或修改器。但是,只需删除您不希望以任何方式访问的少数私有数据成员的方法就足够简单了。代码生成器的重点是不要花费大量时间编写平凡的、可自动化的代码。理想情况下,该语言将允许您为成员声明访问权限。例如,C# 属性。 C++ 没有给你这个,所以代码生成器被用来弥补这个不足。
    • 你可能想思考为什么 C++ 没有给你这个。
    • 您将变量封装在一个类中,因为您想保持它们之间的一致性条件:len + ptr 确实应该指向我的字符串类中的第一个无效字符,最后应该是 ==len- 1等Setters/Getters打破了这个简单的原则;它们通常是通过将“struct”替换为“class”来尝试将 C 代码转换为“面向对象”代码的结果......
    • @ackb:你是对的,但仍然有很多代码专门用于建模业务对象,其中“实体”的大多数“属性”可以在会,并且彼此独立。 (例如客户的电子邮件地址和电话号码。)
    【解决方案3】:

    我无法帮助您确定该特定脚本的位置。但是,有quite a lot 的代码生成工具。您甚至可能已经将您正在寻找的东西作为 IDE 的一部分。有关如何、为什么以及是否使用代码生成工具的更多辩论/意见,您可以查看this stackoverflow question。我喜欢这个问题的公认答案。

    【讨论】:

      【解决方案4】:

      您想要一个脚本不加选择地为所有您的私人成员生成 get/set 函数吗?那不是一个非常有用的脚本;您可能不会在 Google 中找到它。如果您希望能够以某种方式标记您的成员变量并为您自动生成 getter 和/或 setter 骨架,那么 IDE 宏似乎更合适。试试谷歌吧。

      【讨论】:

        【解决方案5】:

        我知道一位程序员 uses Perl to augment the C preprocessor 谈到宏 (latest version of that project)。基本思想是您将决定一些约定来告诉您的 Perl 脚本何时生成 getter 或 setter:

        struct My_struct {
            //set
            //get
            int x;
        
            int y;
        
            //get
            int z;
        };
        

        给定这样的代码,我可以编写一个脚本来在成员变量声明之前的行中查找由注释“get”或“set”组成的注释行,然后用简单的 setter/getter 替换它们.在上面的示例中,我将在关联的成员变量定义之后生成 void set_x(int i)、int get_x() 和 int get_z()。

        警告:不要在原地使用 s/// 执行此操作。相反,单独扫描每一行,如果你找到合适的注释行,将“我需要一个 getter/setter”的内容推送到堆栈上,然后当你看到相关的成员变量时,将内容从堆栈中弹出并生成代码.

        细节上有些小问题,但总的来说就是这样。

        【讨论】:

        • +1。因为解析完全通用的 C++ 声明实际上非常困难,我敢打赌你的 Perl“预处理器”只能识别相对简单的声明的子集,这很好——但我建议你的脚本积极地抱怨它可以的任何声明't decode 所以你把任何问题扼杀在萌芽状态。快速失败总是更好。
        猜你喜欢
        • 2021-06-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-11
        • 2012-08-21
        相关资源
        最近更新 更多