【问题标题】:How usable is Qt without its preprocessing step?如果没有预处理步骤,Qt 的可用性如何?
【发布时间】:2011-04-05 00:39:53
【问题描述】:

我认为图书馆要求使用特殊工具对我的源代码进行预处理是不合理的。也就是说,有几个人向我推荐了 Qt 库,用于跨平台 GUI 开发。

如果没有预处理步骤,Qt 的可用性如何?

编辑:好的,我的意思不是说这个问题是对 Qt 的破坏——太多的 Qt 粉丝正在把它当作是。我不想讨论 Qt 提出这个预处理工具这一事实的优点。我明白为什么会有这个工具,也明白为什么 Qt 的大部分设计都是建立在预处理理念之上的。

我从来没有使用过 Qt,因此我无法撕毁它。但我更愿意自己编写少量样板代码,而不是依赖于拆解我的整个构建过程。出于同样的原因,我不会在我当前的项目中使用 Flex 和 Bison。如果我不使用这些工具,我肯定不会使用另一种预处理。

所以,请不要把我当成是在嘲笑 Qt。我无法评论它有多好或不好。我没用过。我只是想知道没有moc是否可以使用它。

【问题讨论】:

  • +1 他们的moc 工具是我没有加入 Qt 潮流的主要原因。
  • moc 真的只是一个问题,直到你真正使用它。我曾经对额外预处理步骤的想法犹豫不决,但是一旦我开始使用 Qt 而不是仅仅考虑它,moc 就不再是麻烦了。特别是如果您使用 QMake,moc 在实践中几乎是不可见的。
  • Qt 对于 C++ 用户来说不仅仅是“另一个库”。 Qt 认为它是一个完整的、包罗万象的框架,可满足您的所有应用程序开发需求(范围与 Java SE SDK 相当),一旦您采用了它的方式(moc 和所有),您应该永远满足于它的围墙花园.
  • 只是对您的编辑说明的评论。我怀疑你主要是用你的开场白激怒了 Qt 粉丝(比如我自己)。 “我认为这是不合理的……”。因此,您应该不会对告诉您为什么合理的回答感到惊讶。
  • @jkerian:请参阅我对 Steve S 回答的评论。总而言之,对于任何中等复杂的软件解决方案,都有一些事情。 Qt 的其中一件事是需要单独的预处理。我不喜欢 C++ 完全破坏(恕我直言)异常规范。但 C++ 仍然是我最喜欢的语言。我确实认为进行预处理是不合理的。对我来说,这是反对 Qt 的一个主要标志,没有谎言。我认为这很糟糕。这并不意味着我反对使用或喜欢整个图书馆。

标签: c++ qt


【解决方案1】:

Qt 不需要使用 moc 来使用它,如果您创建 QObject 的子类并在您的自定义类中声明信号和槽,则它需要使用它。

不无道理,moc提供了C++没有的特性,信号/槽,自省等。

因此,要进行最低限度的高级操作,您必须使用 moc 预处理器。你要么爱它,要么恨它。

【讨论】:

  • 是否正在创建一个带有选项卡控件、文本框和一些“最低限度高级”按钮的窗口?
  • Boost.Signals2 (boost.org/doc/libs/1_40_0/doc/html/signals2.html) 实现了观察者模式,而无需求助于信号/插槽等特殊预处理。此外,如果您使用预处理器宏从CObject 派生,MFC 会提供自省功能。我并不是说这两种实现都优于 Qt,只是人们在没有生成额外源文件的情况下管理它。
  • @Billy Depends,将按钮单击连接到自定义类中的插槽需要使用 moc。
  • @Matias:所以,我可以有一个按钮,但我无法知道何时有人点击了它?
  • @Praetorian: qts moc 编译器提供的不仅仅是信号/插槽,而且它是在您不能依赖模板的时候编写的,因为模板支持得不太好在所有编译器上
【解决方案2】:

考虑到 Qt 库的庞大和综合性,我认为 Qt 需要特殊的预处理工具并非不合理。

Boost 和 GLib 等其他类似的综合库不需要特殊的预处理工具,但会广泛使用标准 C 预处理器。 Qt 可以只使用 C 预处理器来实现,但是通过使用它自己的特殊预处理工具,它可以提供更简洁的语法并避免许多与 C 预处理器宏相关的缺陷。

正如已经回答的那样,您可以在没有 moc 的情况下使用 Qt,而不是任何需要信号和插槽的东西。是的,这确实包括了所有的 GUI 东西,但 Qt 绝不是只是一个 GUI 库。

【讨论】:

  • -1:这并不能回答我提出的问题。我不是在问 Qt 的优点/缺点。我在问它是否可以在没有特殊预处理工具的情况下使用。
  • (对不起,双重评论)我知道要执行反射之类的操作,他们需要使用预处理,因为 C++ 不提供开箱即用的功能。也就是说,真的没有理由必须以这种方式实现信号和槽——有很多纯 C++ 解决方案可以解决这个问题。不过,我明白,为了实现 Qt 最初的目标,需要进行预处理。但鉴于我不打算使用任何类型的反射功能,我想知道是否可以在不进行预处理的情况下合理使用该库。
  • 除了已经发布的内容之外,我不确定您还在寻找什么。答案很简单,您可以“有效地使用”Qt 中不依赖 moc 来编译或正常运行的部分。这对您来说是否足够取决于您使用 Qt 做什么。听起来你正在制作一个 GUI,所以你可能无法避免避免 moc。
  • 不知何故我错过了你回答中的最后一段......当我发表评论时它在那里吗?无论如何...我只是不希望这个问题变成 Qt Fanboys 与 Qt Haters 的火焰战争。 (请参阅对我的问题的编辑,解释我来自哪里)。我认为这就是这个答案的来源;我已将我的反对票改为赞成票。
  • 您不认为这是不合理的,但是构建在 QT 之上的库的最终用户呢?他们是否会认为他们不能简单地在他们最喜欢的编译器中使用您的代码而不下载和安装额外的构建工具是合理的? IMO 这比开发人员可能面临的任何不便都重要得多。
【解决方案3】:

如果不使用 QObjects,我真的想不出任何对 Qt 如此独特和有用的东西。我希望他们能按自己的方式解决预编译步骤。

【讨论】:

    【解决方案4】:

    有可能吗?只要你不做任何 gui 编程,可能。就个人而言,这些天我主要使用 PyQt 运行,所以这对我来说不是一个大问题。

    为什么你不应该关心: 如果您使用 cmake 或 qmake,则考虑到“预编译”的性质,就不便而言,这并不是什么大问题。如果你现在用 GUI 做任何事情,你应该在大部分工作中使用图形设计器,所以你已经添加了一些“预编译”步骤。

    关于他们为什么这样做: 你可能有兴趣阅读 Qt 的解释:http://doc.qt.io/qt-4.8/templates.html

    归结为:

    • 模板解决方案缺少属性和重载
    • moc 解决方案可防止信号破坏二进制兼容性
    • 可以使用非模板化的信号/槽机制进行运行时分析和修改

    另一方面,多线程信号/插槽也是他们系统的一个优势。

    【讨论】:

    • 这很重要,因为我必须拆除现有的构建系统并用 Qt 替换它。我不想花几个小时从 MSBuild 转换。
    • 如果它首先是一个半体面的构建系统,它会适应它。 linuxdummy.blogspot.com/2008/06/…
    • 鉴于这种反对意见,我怀疑 Qt 可能不适合您的问题。 Qt 是一个非常庞大的库,我不确定它作为一个“插件”到一个可能很大的项目中的效果如何。
    • 基本上可以教任何构建系统如何处理 moc 和预编译步骤,使用 cmake 或 qmake 更容易。我已经用自定义的makefile做了几次了。不一定令人愉快,但可行。
    • 如果其他人从 Google 访问此页面(就像我一样),我会注意到我能够使用 MSBuild 将 Qt 添加到现有的基于 MFC 的应用程序中,大约需要 40 小时.我们的 MSBuild 设置在大约 150 个 VS 项目中处理了大约 150 万行 C++,但让 Qt 使用它仍然很容易。 Qt 有一个用于 Visual Studio 的附加组件,它使 moc 的管理大部分自动化。
    【解决方案5】:

    在避免moc 的同时使用 Qt 将比按预期将它们一起使用更困难。您还将牺牲大部分激励其他人推荐 Qt 的有趣特性。

    没有moc你不能

    • 使用信号和槽(这些都是 UI 所必需的)
    • 使用动态属性系统(需要编写插件等)
    • 使用国际化功能
    • 当无济于事时,希望得到任何人的帮助

    如果你想使用 Qt,请使用moc。事实上,甚至不用担心moc——只需使用 QMake。您可以编写如下所示的 QMake .pro 文件:

    TARGET = myApp
    
    FORMS += MainWindow.ui
    
    HEADERS += MainWindow.h
    
    SOURCES += MainWindow.cpp
    SOURCES += main.cpp
    

    一切都会自动处理。或者你可以把所有的时间都花在想办法避免moc

    https://doc.qt.io/archives/qt-4.7/metaobjects.htmlhttps://doc.qt.io/archives/qt-4.7/moc.html#moc

    【讨论】:

    • 我不想为了使用单个库而改造整个构建系统(目前是 MSBuild)。
    • 1) 您没有声明您正在转换现有项目。 2) 您可以让 MSBuild 与 moc 一起工作比让 Qt 在没有 moc 的情况下工作更容易。
    • 我假设如果我说“我认为使用预处理工具是不合理的”——这并不是那么具有侵略性——这有点暗示我不想花钱一周完全取代了我当前的构建系统(更具侵入性)。鉴于我没有问如何moc在我的项目中工作,我问的是是否有可能避免它,我不认为那条信息与我的问题有关。鉴于您在没有该信息的情况下在答案的第一部分回答了我的问题,我认为我没有错。
    • 你的问题确实很清楚。但鉴于您提供的详细信息,我认为我的回答(全部)是适当的。即使有附加信息,我的建议仍然是“使用 moc,或者根本不用 Qt。”
    • 实际上,使用任何带有moc 的构建系统并不难。
    【解决方案6】:

    我没有完整的答案,但据我了解,moc 主要(或可能仅)生成额外的 C++ 代码。因此,它可能没有什么是你不能自己手动完成的。但是,我不知道这可能有多乏味,也不知道要了解该代码中所有必要的概念和细节可能需要进行多少研究。


    另外,正如我的旁注:在我看来,您之所以对 Qt 和 moc 进行如此多的辩护是因为您以措辞强硬的“我认为这是不合理的”开始您的问题,这很容易解释为你认为 moc 不应该存在。这会分散您的实际问题的注意力。我认为最好只说“moc 不适合我的构建系统”或简单地说“我有自己的理由不想使用它”。

    【讨论】:

    • 我确实认为这是不合理的。这并不意味着我不喜欢整个 Qt。这确实意味着我非常不喜欢必须使用单独的预处理器。我也非常不喜欢关于 C++ 规范的很多东西(即异常规范)——这并不意味着 C++ 不是我最喜欢的语言。任何足够复杂的技术都会有烦人的部分。这绝不是 Qt 独有的。我认为阅读我的问题的人会理解这一点。也许我假设太多了。
    【解决方案7】:

    你可以在没有 moc 的情况下使用 Qt,但是你会失去某些功能,尤其是那些让 Qt 变得有趣的功能(例如大多数 GUI 内容、信号和插槽以及字符串转换)。但它仍然是一个不错的通用库,即使没有 moc。

    【讨论】:

      【解决方案8】:

      我目前需要寻找 MOC 的替代品。到目前为止,我使用 GLib 进行文本翻译,并且正在使用 C++ 模板和 C 宏组合重新设计我在网络上找到的信号/插槽库 (sigslot)。枯燥乏味的部分是重做我的应用程序的 GUI,并通过小部件升级(Qt Designer 的一个功能)用我自己的小部件(即 QPushButton -> MyPushButton)替换普通的 Qt 小部件。这样我可以让我的小部件发出模板信号而不是 Qt。有一个问题。我的“修改过的”小部件类必须通过预处理器运行,但这是千载难逢的一步。之后,我可以继续前进。

      我能负担得起这一切只是因为我已经编写了一个应用程序框架库,它有自己的事件循环(嗯,它是一个搭载 Qt 事件循环代码的包装器)、多线程和字符串类等。我的项目只需要 Qt 来显示漂亮漂亮的小部件。

      但是,如果您没有这些工具可供您使用 - 我敢肯定您没有 - 相信我,试图摆脱 Qt 的预处理器将是一件非常痛苦的事情。

      这是我给您的信息:如果您可以使用 QMake 或 moc,请使用它们。

      【讨论】:

      • 我还没有编写任何 Qt 代码,也不想依赖外部工具,因此提出了这个问题。 (你可以用boost::signals 之类的东西来做信号/槽;)
      【解决方案9】:

      (抱歉恢复这么旧的帖子)

      我被要求为我的 3D 软件开发理学硕士单元做 C/W 作业。

      不幸的是,需要使用 Qt 来提供 OpenGL 上下文,而不是使用 Glut、本机 X11、Gtk 等...

      我不想使用 MOC,并且经过大量的摆弄,我能够获得足够的回调(例如键盘、鼠标、绘画、计时器等...)来进行可用的提交。 因此,将 Qt 与 OpenGL 软件一起使用的主要原因,它实际上在没有 MOC 的情况下也能正常工作。

      但是,如果不使用 MOC,我看不出如何开发完整的应用程序。真可惜。

      这个 Qt MOC 东西很痛苦。我不明白为什么这么多 C++ 开发人员似乎坦率地认为它是可以接受的。它非常不便携,而且会很快变硬! (试着编译一些 Borland Kylix C++ 代码。你很快就会意识到这是一个多么糟糕的主意)。

      如果我想使用非标准 C++,我只会使用 Microsoft C++/CLI。

      【讨论】:

      • 我不会对你投反对票,但你的答案的后半部分非常主观且不必要地批评。您可以在“但是”这句话之后停下来,这将是一个很好的、有帮助的答案。
      【解决方案10】:

      如果您只需要连接来自 Qt 对象的信号,黑客解决方案是利用现有的 QT 对象,这些对象具有与您要连接的信号的签名相匹配的公共或受保护的虚拟插槽。您可以子类化 QT 对象并将虚拟插槽重新实现为代理,以在发出 QT 信号时执行您需要的任何操作。例如,

      class SignalProxy : public QWidget
      {
      public:
        SignalProxy() {}
      
        void setVisible( bool isVisible )
        {
           // Do whatever you want to do when the signal is emitted.
        }
      };
      
      
      // code to connect the signal, e.g., to a QWebView object
      SignalProxy proxy;
      QWebView webview;
      QObject::connect( &webview, SIGNAL(loadFinished(bool)),
              &proxy, SLOT(setVisible(bool)) );
      

      它并不漂亮,但它完成了工作。如果您真的打算不使用 MOC,您可能会找到现有的 Qt 对象以用作您需要的任何信号签名的代理,例如,QAbstract... 类有很多虚拟插槽,您可以隐藏所有这些提供 boost 信号或 tr1::function 样式 API 以连接到 QT 信号的库中的污点。

      在 QT 对象上调用槽比接收信号要少,因为您通常可以直接调用槽方法。

      【讨论】:

        【解决方案11】:

        我有一个解决方案,它不是超级干净,也不是 100% 令人满意,但它允许将 Qt 信号连接到您自己的代码,而无需使用 MOC 编译器(我的约束与问题中的完全相同,即无法在我的应用程序的构建过程中运行 MOC 编译器)。

        为了能够在不使用 MOC 的情况下捕获 Qt 信号,我使用了以下技巧:

        (1) 获取 QMetaCallEvent 的定义(从 复制): 在 Qt 5.x 中,您将拥有如下内容:

        class QMetaCallEvent : public QEvent {
        public:
            inline int id() const {
                return method_offset_ + method_relative_;
            }
        
            virtual void placeMetaCall(QObject *object);
        
        private:
            QMetaCallEvent();
            void* slotObj_;
            const QObject *sender_;
            int signalId_;
            int nargs_;
            int *types_;
            void **args_;
            void *semaphore_;
            void *callFunction_;
            ushort method_offset_;
            ushort method_relative_;
        };
        

        (2) 在需要捕获 Qt 信号的小部件类中,您将继承自 Qt 小部件(例如 QButton),并定义以下函数:

        // Inspired by QObject::connect() in src/corelib/kernel/qobject.cpp
        bool connect_sender(
            const QObject* sender, const char* signal, int method_index
        ) {
        
                // We need to generate MetaCall events (since QObject::event()
                //   is the only virtual function we can overload)
                // (note that the other connection types do not generate events).
                Qt::ConnectionType type = Qt::QueuedConnection ;
        
                if(sender == 0 || signal == 0) {
                    std::cerr << "null sender or signal" << std::endl ;
                    return false ;
                }
        
                QByteArray tmp_signal_name;
                const QMetaObject *smeta = sender->metaObject();
                ++signal; //skip code
                int signal_index = smeta->indexOfSignal(signal);
                if (signal_index < 0) {
                    // check for normalized signatures
                    tmp_signal_name = 
                        QMetaObject::normalizedSignature(signal).prepend(*(signal - 1));
                    signal = tmp_signal_name.constData() + 1;
                    signal_index = smeta->indexOfSignal(signal);
                    if (signal_index < 0) {
                        std::cerr << "Signal \'" << signal << "\' not found" 
                                  << std::endl ;
                        return false;
                    }
                }
        
                int *types = 0;
        
                QMetaObject::connect(
                    sender, signal_index, this, method_index, type, types
                ) ;
        
                return true ;
            }
        

        (3)重载event()函数:

        bool event(QEvent* e) {
            if(e->type() == QEvent::MetaCall) {
                QMetaCallEvent* ev = static_cast<QMetaCallEvent*>(e);
                switch(ev->id()) {
                    // insert your handling code here
                }  
                return true;
            }
            return QObject::event(e) ;
        }
        

        现在,如果你调用 connect_sender(qobject, signal_name, method_index),这个 每次触发信号时都会调用 event(),并在 ev->id() 中检索指定的 method_index。

        重要提示: 几年来我一直在我的应用程序中使用这个技巧,效果很好,但不是很干净。后果之一是,每当 QMetaCallEvent 的定义发生变化时,您都需要相应地编辑您的声明(不幸的是,它没有在 Qt 的头文件中公开)。

        【讨论】:

        • 我知道这是一个老问题,但我认为这个技术答案可能对有同样问题的人感兴趣....
        【解决方案12】:

        现在完全可以使用了。 moc 的维护者做了一个替代方案,它的语法比普通 Qt 稍微冗长一些,但它使用标准 C++14,所以没有额外的步骤。

        It's called 'Verdigris'

        (顺便说一句,moc 与其说是代码生成器,不如说是一个预处理步骤。您编写的代码是有效的 C++,moc 不会更改任何内容。它只是为您生成额外的 C++ 代码。 )

        【讨论】:

        • RE:除此之外:“公共插槽”不是有效的 C++。 Qt 与 yacc 不同的是 C++。
        • 如果你在某处有#define slot,那么预处理器会将其转换为简单的public,这正是Qt 所做的。 moc 不会编辑您的任何源文件,它只会创建额外的 moc_myfile.cpp 文件。编译使用您编写的确切头文件和 cpp 文件,以及 moc_*.cpp 文件。
        • @BillyONEal qt 和 yacc 我认为来自不同的马厩。一个经典,另一个是黑客。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-06-05
        • 1970-01-01
        • 2023-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多