【问题标题】:Automatically replace assignment operator with "set" function用“set”函数自动替换赋值运算符
【发布时间】:2014-08-29 09:06:50
【问题描述】:

我正在将使用 Embarcadero RAD Studio 构建的 C++ Windows 项目移植到带有 Qt 的 Linux。 所以我的代码中有很多这样的语句:

Menu->Enabled = true;

对于 Qt 应该这样转换:

Menu->setEnabled(true);

所以现在我浪费了很多时间来评论代码。有没有办法自动进行这种替换?

【问题讨论】:

  • 好吧,我实际上不会回答你的问题,但过度使用 setter 通常是不好的。如果有一个公共成员,比如这里的 enabled,你不应该为它写一个 setter,除非它做的不仅仅是给它新的值。 (比如检查它是否是一个有效的新值)老实说,setter 可以帮助你调试,所以它很有用,但是对于一个逻辑上的公共成员我不喜欢它。
  • 写得好的正则表达式替换是最简单的解决方案。我不建议您使用宏(如果可能的话)。
  • 编写一个包装类,为 Qt 提供不同的实现/任何你需要的与 Qt 不兼容的东西。
  • @juanchopanza setEnabled() 由许多基于 QWidget 的 Qt 类所拥有,因此为所有类编写包装器非常繁琐。
  • 我会使用编译指令。在 Borland 时代,定义了 __BORLANDC__ 宏。您可以在与您要根据平台修改的部分相关的代码周围使用#ifded #ifdef __BORLANDC__#else#endif 子句。我猜 Embarcadero 保留了这个宏或者定义了它的新版本。在我看来,这样的指令在 QT 中有点棘手。谷歌搜索建议使用宏 QT_VERSION

标签: c++ linux windows qt


【解决方案1】:

如果这是一些次要的移植细节,会妨碍处理更重要的问题,在这个移植项目中,您还可以考虑将这个重构推迟到以后,使用类似这样的方法:

class Menu {

public:

      class punt_enabled {
          Menu *me;
      public:
          punt_enabled(Menu *meArg);
          Menu &operator=(bool flag);
      };

      punt_enabled Enabled;

 // ...

 };

 // ...

 Menu::Menu(...) : Enabled(this) //...

 Menu::punt_enabled::punt_enabled(Menu *meArg) : me(meArg) {}

 Menu &Menu::punt_enabled(bool flag) { me->setEnabled(flag); return *me; }

 // ...

这将使编译器立即执行此替换,然后您可以返回并完成工作,使用 grep+sed 或 perl 来处理批量替换。

附: punt_enabled 需要由所有构造函数初始化,当然包括复制和移动构造函数。

【讨论】:

  • 您不需要破解或覆盖 Qt 来执行此操作吗?
【解决方案2】:

无论您是在 Linux 操作系统中还是在 Windows 中安装了 MinGW 或 Cygwin,要运行 Unix 命令,请在您要更改代码的基本文件夹中执行以下行:

grep -rl "Menu->Enabled = true;" ./ | xargs sed -i "s/Menu->Enabled = true;/Menu->setEnabled(true);/g"

注意:此命令适用于保存在基本文件夹和子文件夹中的所有文件

编辑:执行以下命令,应用到所有使用“->Enabled = true”或类似格式的变量:

grep -rl -e "->Enabled = true;" ./ | xargs sed -i "s/->Enabled = true;/->setEnabled(true);/g"

【讨论】:

  • 如果不是每个变量都称为Menu,或者格式不同,或者包含右侧的表达式怎么办?
【解决方案3】:

通常我会加入并进行更改,引导一些强迫症,直到构建干净为止。 为了帮助采用这种方法,这里有一些提示:

首先,更改成员的名称,而不仅仅是将其设为私有。至少暂时使名称成为代码中的唯一标记(您可以稍后进行愚蠢的全局替换以将其命名为您想要的名称)。

其次,许多这样的行具有完全相同的格式甚至相同的变量名称,因为它们是由具有统一约定的人编写的,甚至是从其他出现的地方复制而来的。这意味着一系列愚蠢的搜索/替换命令将找到许多匹配项。找到这样一个需要更改的行,并在与其中有趣部分匹配的所有文件中发出全局替换。它不需要完全通用,因为您将重复仍然存在的下一个变体。假命中怎么办?即使在其他类中也能找到具有该名称的每个字段——如果这很常见,那又如何?也向该类添加一个 setXXX 成员函数,其工作方式与公共字段相同。现在替换是无害的,它也可能激发进一步的封装。

第三,使用本地 git 分支并在每次批量操作后签入更改。这使得稍后撤消或修复变得容易,或者只是返回并查看之前所说的内容。

如果这不是智能指针而是原始指针,那么 Source Insight 编辑器可能会正确地对字段进行智能重命名,主要是。然后使用正则表达式通过函数调用批量更改新的唯一名称。

最后,您可以检查一下编译器扩展是否与 Microsoft 一样可用,它们会自动实现属性函数。

但是,您希望通过某种方式使现有语法正常工作(或者完全自动且正确)。所以这里的想法是:使用一个在其上定义了 operator= 的代理类。像这样的:

class C {
public:
   // int value;  -- OLD
   magic_int value;  // NEW
   ⋮
};

C 的构造函数需要使用指向封闭类的指针来初始化所有 magic 字段,因为 C++ 没有像 Java 那样的自动“内部类”。所以

C::C (whatever-it-did-before)
: existing-stuff,
   value(this,&C::setter)  // for each newly magic field

现在您希望字段上的赋值语义调用 setter。所以我们有类似的东西:

int magic_int::operator= (int x)
{
(owner->*setfunc)(x);
}

这意味着 magic_int 需要 owner 和 setfunc 的成员数据,如构造函数中所示。使其成为模板意味着使用 CRTP 并为其提供外部类类型和字段类型:

template<typename OwnerT, typename ValueT>
class magic {
   friend class OwnerT;
   ValueT value;  // the real backing storage, if any.
   OwnerT* owner;
   void (OwnerT::setfunc(const ValueT&));
   ⋮      
};

现在这样安排,以便 C 的指定成员函数 setter 可以做任何需要协调设置的事情,并且作为其中的一部分,它直接设置原始支持值。支持值存储在魔术包装器中以简化作为隐式转换运算符的哑 getter。

此外,为了更有趣,设置代码选择可以通过模板参数完成,因此魔术包装器不需要在实例中存储实际的 setfunc 值。这将减少大量数据开销(指向成员的指针很大)并且还允许 operator= 调用在编译时内联实际调用。

玩得开心。

【讨论】:

  • 理解这个想法需要一些时间:)
  • 同样的想法已被用于允许诸如 operator[] 之类的另一个操作看似“知道”它是用作读取还是写入。我已经使用它来支持轻松使用嵌套集合,这些集合仅在您通过分配实际创建项目时才创建中间级别,而不仅仅是检查它是否存在或读取默认值。如果您需要更多讨论来了解它,请告诉我。 :) 首先了解代理类(参见 std::vector 以了解类似用途)本身,然后了解这种特殊魔法的核心{},然后是使其成为模板的方法。
【解决方案4】:

您可以使用 Qt Creator 查找和替换工具来自动替换整个项目中的特定模式。按Ctrl+Shift+F 或选择Edit &gt; Find/Replace &gt; Advanced Find &gt; Open Advanced Find。 选择搜索范围并按Search &amp; Replace 按钮。然后,您可以指定要替换的文本。

另一种选择是在选定的短语上按Ctrl+Shift+R 并输入替换字符串。

【讨论】:

    猜你喜欢
    • 2015-02-25
    • 2011-08-02
    • 2016-05-19
    • 2017-01-14
    • 1970-01-01
    • 2017-11-21
    • 2011-02-20
    • 2014-02-17
    • 1970-01-01
    相关资源
    最近更新 更多