sigc::ptr_fun()とsigc::mem_fun()

概要

sigc::ptr_fun()とsigc::mem_fun()の違いについて述べる。

これまでは単にシグナルとスロット関数をコネクトするとだけ書いていたが、正しくはコネクトするのは スロット関数ではなくスロットオブジェクトである。従って、connectするときに

signal_foo.connect( &on_foo );
の様に直接スロット関数のアドレスを指定するとエラーが出る。

スロット関数(又はシグナルハンドラ)からスロットオブジェクトを作るには sigc::mem_fun() 又は sigc::ptr_fun() を使用する。なお正確にはこれらの関数はsigc::pointer_functorsオブジェクト を作るものであるが、スロットオブジェクトと一対一で対応しているので同一視することにする。 これらの違いは次の通りである。

・ sigc::ptr_fun() → グローバル関数をシグナルハンドラにする

・ sigc::mem_fun() → あるオブジェクト(もちろん*thisでも可)のメンバ関数をシグナルハンドラにする

従ってsigc::mem_fun()にはオブジェクトとメンバ関数のアドレスの2つの引数が必要とされる。

ソース

sig1.cpp

#include <gtkmm.h>

// グローバルなシグナルハンドラ
void on_clicked_global(){ Gtk::MessageDialog( "グローバルなシグナルハンドラ" ).run(); }

// あるクラスのメンバ関数としてのシグナルハンドラ
class ClickClass
{
public:
    void on_clicked(){ Gtk::MessageDialog( "他のオブジェクト内のシグナルハンドラ" ).run(); }
};


class MainWin : public Gtk::Window
{
    Gtk::Button m_bt, m_bt2;
    Gtk::VBox m_vbox;
    ClickClass m_click;

public:
    MainWin();
};


MainWin::MainWin()
    : m_bt( "グローバル" ), m_bt2( "他のオブジェクト" )
{
    m_vbox.pack_start( m_bt );
    m_vbox.pack_start( m_bt2 );

    // グローバルなシグナルハンドラとコネクト
    m_bt.signal_clicked().connect( sigc::ptr_fun( &on_clicked_global ) );

    // 他のオブジェクト内のシグナルハンドラとコネクト
    m_bt2.signal_clicked().connect( sigc::mem_fun( m_click, &ClickClass::on_clicked ) );

    add( m_vbox );
    show_all_children();
    resize( 200, 100 );
}


int main( int argc, char *argv[] )
{
    Gtk::Main kit( argc, argv );
    MainWin mainwin;
    Gtk::Main::run( mainwin );

    return 0;
}

コンパイル

必要なコンパイルオプションは pkg-config を使って取得する。

g++ sig1.cpp -o sig1 `pkg-config gtkmm-2.4 --cflags --libs`

結果