Gobble up pudding

プログラミングの記事がメインのブログです。

MENU

std::function, std::bindは便利

スポンサードリンク

f:id:fa11enprince:20200701031122j:plain
C++でstd::function, std::bindを使ってみました。

あんまりJavaScriptは詳しくないですが、
例えばJavaScriptではこんなことができます。

<html>
    <head>
        <script type="text/javascript">
            <!--
            var funcs =
            [
                function() {
                    window.alert('1');
                },
                function() {
                    window.alert('2');
                },
                function() {
                    window.alert('3');
                }
            ];
            // 突っ込んだ順に呼び出す。
            window.onload = (function() {
                window.alert('hoge called.');
                while (funcs.length > 0) {
                    funcs.shift()();
                }
            }());
            // -->
        </script>
    </head>
    <body></body>
</html>

変数に関数を突っ込んでそれを実行するようなことができます。
JavaScriptでは引数が違う場合はどうすんのか知らない...
即時関数使えばできた。

var funcs =
[
    (function() {
        window.alert('1');
    }()),
    (function() {
        window.alert('2');
    }()),
    (function(x) {
        window.alert(x);
    }('3'))
];

こう書けばいいだけだった。顔文字みたいのがありますね。。。
C++ではできないのか???
もちろんできます。
最初の例のような程度なら関数ポインタの配列で実現できます。
C++11からはもっと一歩進んだ使い方ができます。
それがstd::functionとstd::bindです。
いまいち使い道が…というのはありますが、例えば僕が思いついた例で
適切かどうかはわかりませんが、プログレスバーなどで
とある関数を呼んだあとに、プログレスバーを更新・表示する処理を呼び出す
等といったときです。
この場合、プログレスバーの最大値を決めてやらねばいかないですが、
普通はconstなどで定数で決めてしまってあとは……とダサいことになり、
そこの処理が増えると定数も書き換えなんてことになっていまいそうです。
std::functionを使えばうまくいけそうです。
ソースを出したほうが早そうなので、
ということでソースコードです。ちなみにg++を使っています。

ソースコード

#include <functional>
#include <iostream>
#include <iomanip>
#include <vector>
#include <unistd.h>

using namespace std;

void slow_process_1()
{
    sleep(1);
    cout << "[" << __func__ << "]" << endl;
}

void slow_process_1(int x)
{
    sleep(1);
    cout << "[" << __func__ << "]" << endl;
}

void slow_process_2(int x)
{
    sleep(1);
    cout << "[" << __func__ << "]" << endl;
}

void slow_process_3(int x, int y)
{
    sleep(1);
    cout << "[" << __func__ << "]" << endl;
}

void slow_process_4()
{
    sleep(2);
    cout << "[" << __func__ << "]" << endl;
}

void show_progress(float current, float meterMax)
{
    int percent = static_cast<int>(current / meterMax * 100);
    cout << "progress : " << setw(3)
        << percent << "%" << endl;
    if (percent == 100)
    {
        cout << "complete!" << endl;
    }
}

int main()
{
    // 全部void ()の形にしてやる
    vector<function<void ()>> vf =
    {
        bind(static_cast<void (&)()>(slow_process_1)),
        bind(static_cast<void (&)(int)>(slow_process_1), 100),
        bind(slow_process_2, 100),
        bind(slow_process_3, 100, 200),
        bind(slow_process_4)
    };

    float meterMax = static_cast<float>(vf.size());
    float cnt = 1.0;
    for (auto f : vf)
    {
        f();
        show_progress(cnt++, meterMax); 
    }

    return 0;
}

実行結果

[slow_process_1]
progress :  20%
[slow_process_1]
progress :  40%
[slow_process_2]
progress :  60%
[slow_process_3]
progress :  80%
[slow_process_4]
progress : 100%
complete!

最初の関数でキャストしているのはオーバーロードしていて、
bindする側がどっちかわかんねーよ!!って激おこになってしまうため
どっちなのか教えてあげるためにキャストしています。
ちなみにここではstd::bindの一部機能しか使っていませんが、
もっと高度なことができます。
std::placeholderとか便利そうですよ。

参考

オーバーロードされている関数の解決法

c++ - std::bind overload resolution - Stack Overflow
c++ - std::bind and overloaded function - Stack Overflow
c++ - use std::bind with overloaded functions - Stack Overflow

ちなみにプレーンな関数(つまりクラスに属していない関数)ではオーバーロードしたときは
Cスタイルのキャストではまぁ、普通にキャストしてやればいいんですが、
C++スタイルのstatic_castを使ってキャストするときは
void (&)()のように(&)が必要だそうです。
なんじゃこりゃって感じですが、どうも関数のポインタですよって知らせてるみたい。
void (*)()でも同じ意味だそうだ。