gtkmm中signal_draw信号处理函数为何必须返回int?返回0是否合规?
问题
正在开发一个展示谢尔宾斯基三角形的程序,首次使用Cairo库,先编写了绘制单条线的演示代码。发现绑定到signal_draw信号的on_D_Triangle_draw函数必须返回int类型,若设为void会触发编译错误(错误信息附后),现咨询该现象的原因,以及返回0是否符合最佳实践。
代码示例
#include <glibmm.h> #include <gtkmm.h> Gtk::Window* W_Main; Gtk::DrawingArea* D_Triangle; Gtk::Button* B_Draw; Gtk::Adjustment* A_Depth; int on_D_Triangle_draw(const Cairo::RefPtr<Cairo::Context> cr) { cr->set_source_rgb(0.0, 0.0, 0.0); cr->set_line_width(4.0); cr->move_to(0, 0); cr->line_to(30, 30); cr->stroke(); return 0; } int main(int argc, char** argv) { auto app = Gtk::Application::create(argc, argv, "org.CENSORED.sierpinski"); auto builder = Gtk::Builder::create_from_file("sierpinski.glade"); builder->get_widget("W_Main", W_Main); builder->get_widget("D_Triangle", D_Triangle); builder->get_widget("B_Draw", B_Draw); builder->get_widget("A_Depth", A_Depth); D_Triangle->signal_draw().connect(sigc::ptr_fun(on_D_Triangle_draw)); app->run(*W_Main); }
编译错误信息
In file included from /usr/include/sigc++-2.0/sigc++/signal_base.h:27, from /usr/include/sigc++-2.0/sigc++/signal.h:8, from /usr/include/sigc++-2.0/sigc++/sigc++.h:123, from /usr/include/glibmm-2.4/glibmm/thread.h:50, from /usr/include/glibmm-2.4/glibmm.h:103, from sierpinski.cpp:1: /usr/include/sigc++-2.0/sigc++/functors/slot.h: In instantiation of ‘static T_return sigc::internal::slot_call1<T_functor, T_return, T_arg1>::call_it(sigc::internal::slot_rep*, sigc::type_trait_take_t<T_arg3>) [with T_functor = sigc::pointer_functor1<Cairo::RefPtr<Cairo::Context>, void>; T_return = bool; T_arg1 = const Cairo::RefPtr<Cairo::Context>&; sigc::type_trait_take_t<T_arg3> = const Cairo::RefPtr<Cairo::Context>&]’: /usr/include/sigc++-2.0/sigc++/functors/slot.h:177:56: required from ‘static void* (* sigc::internal::slot_call1<T_functor, T_return, T_arg1>::address())(void*) [with T_functor = sigc::pointer_functor1<Cairo::RefPtr<Cairo::Context>, void>; T_return = bool; T_arg1 = const Cairo::RefPtr<Cairo::Context>&; sigc::internal::hook = void* (*)(void*)]’: /usr/include/sigc++-2.0/sigc++/functors/slot.h:679:90: required from ‘sigc::slot1<T_return, T_arg1>::slot1(const T_functor&) [with T_functor = sigc::pointer_functor1<Cairo::RefPtr<Cairo::Context>, void>; T_return = bool; T_arg1 = const Cairo::RefPtr<Cairo::Context>&]’: /usr/include/sigc++-2.0/sigc++/functors/slot.h:1843:26: required from ‘sigc::slot<T_return, T_arg1, sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil>::slot(const T_functor&) [with T_functor = sigc::pointer_functor1<Cairo::RefPtr<Cairo::Context>, void>; T_return = bool; T_arg1 = const Cairo::RefPtr<Cairo::Context>&]’: sierpinski.cpp:40:38: required from here /usr/include/sigc++-2.0/sigc++/functors/slot.h:170:16: error: void value not ignored as it ought to be 169 | return (typed_rep->functor_).SIGC_WORKAROUND_OPERATOR_PARENTHESES<type_trait_take_t<T_arg1>> | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 170 | (a_1); | ^~~~~
回答
错误原因
Gtk::DrawingArea的signal_draw信号要求回调函数返回bool类型(你用int能编译是因为C++中int和bool可以隐式转换,但这不是规范写法)。从编译错误信息里能看到T_return = bool,说明信号机制期望得到一个布尔值。
当你把回调设为void返回时,sigc绑定框架会尝试将void返回值当作bool来接收,这就触发了编译错误——C不允许忽略void值去赋值给需要bool的变量,也就是错误提示里的“void value not ignored as it ought to be”。
这个返回值的作用是告诉GTK是否已经处理完绘制事件:
- 返回
true(或非0的int):表示你已经完成了所有绘制工作,GTK不需要再执行默认的绘制逻辑(比如清除背景) - 返回
false(或0):表示你只做了部分绘制,需要GTK完成剩余的默认绘制动作
最佳实践
你返回0(等价于false)是合理的场景之一:如果你的回调只绘制了线条,需要GTK帮忙处理背景清除等默认操作,返回0没问题。
但有两个优化建议:
- 把回调函数的返回类型改成
bool,这完全符合GTKmm的接口定义,代码可读性更强,也能避免隐式转换带来的潜在问题 - 如果你后续的谢尔宾斯基三角形绘制会覆盖整个DrawingArea区域,应该返回
true(或1),这样GTK不会重复执行默认绘制,能节省资源
内容的提问来源于stack exchange,提问作者AlgebraicsAnonymous
相关产品推荐
相关产品推荐

