LoginSignup
1
1

More than 1 year has passed since last update.

【C++11~】タイムアウト機能付きメンバ関数

Last updated at Posted at 2021-06-10

仕様

 こんな関数を作りたい

処理結果 is_timeout 返り値
成功 false true
失敗 false false
タイムアウト true false

 特に組み込み系の設計では、数行程度でポーリング処理を単純化して書けるので、あると超便利である。

方針

  • クラス"SubFunction"の中に、クラスSourceClass1, SourceClass2のオブジェクトを生成する

    • テンプレートを使って、双方のクラスの参照を許可する
    • 補足:生成せずに参照する場合は、SubFunctionコンストラクタを見直す
  • 内部処理は、std::asyncで記述して関数の入力/返り値の対応関係の見通しを良くして記載する。

    • 補足:wait_forを使用しなければ非同期
  • タイムアウトかどうかを判定するのは、std::futureの返り値を使用する。

ソースコード

SourceClass.h

#pragma once

namespace Timeout {
    class SourceClass1 {
    public:
        SourceClass1() {

        }

        virtual ~SourceClass1() {

        }
    };

    class SourceClass2 {
    public:
        SourceClass2() {

        }

        virtual ~SourceClass2() {

        }
    };
}


SubFunction.h
#pragma once


#include <chrono>
#include <functional>



namespace Timeout {

    template <typename Target>
    class SubFunction {
    private:
        Target* _target;
    public:
        SubFunction();

        virtual bool WaitForRunning(std::function<bool(Target*, bool&)> wait_func, std::chrono::milliseconds duration, bool& is_timeout);
    private:
        bool _WaitForRunning(std::function<bool(bool&)> func,std::chrono::milliseconds duration, bool& is_timeout);
    };

}




SubFunction.cpp

#include <future>

#include "SubFunction.h"

#include "SourceClass1.h"
#include "SourceClass2.h"


using namespace Timeout;

SubFunction<SourceClass1>::SubFunction() {
    _target = new SourceClass1();
}



SubFunction<SourceClass2>::SubFunction() {
    _target = new SourceClass2();
}


bool SubFunction<SourceClass1>::WaitForRunning(std::function<bool(SourceClass1*, bool&)> wait_func, std::chrono::milliseconds duration, bool& is_timeout) {
    auto func_internal = [this, &wait_func] (bool& is_done) {
        return wait_func(_target, is_done);
    };

    return _WaitForRunning(func_internal, duration, is_timeout);
}

bool SubFunction<SourceClass2>::WaitForRunning(std::function<bool(SourceClass2*, bool&)> wait_func, std::chrono::milliseconds duration, bool& is_timeout) {
    auto func_internal = [this, &wait_func] (bool &is_done) {
        return wait_func(_target, is_done);
    };

    return _WaitForRunning(func_internal, duration, is_timeout);
}


template <typename Target>
bool SubFunction<Target>::_WaitForRunning(std::function<bool(bool&)> func, std::chrono::milliseconds duration, bool& is_timeout) {
    is_timeout = false;
    bool is_succeed = false;
    bool is_done = false;

    // ラムダ式で処理を実行
    // 終了したらis_doneをtrueにする
    std::future<bool> future_wait = std::async(std::launch::async, [this, func, &is_timeout, &is_succeed, &is_done]() {
        while (!is_timeout && !is_done) {
            is_succeed = func( is_done);
        }
        return is_done;
    });

    // ポーリング
    auto timeout_status = future_wait.wait_for(duration);

    // タイムアウト
    if (timeout_status == std::future_status::timeout) {
        is_timeout = true;
        return false;
    } else {
        return is_succeed;
    }

    return true;
}

main.cpp

#include <windows.h>
#include <iostream>

#include "SourceClass1.h"
#include "SubFunction.h"

int main() {
    auto subfunc_obj = new Timeout::SubFunction<Timeout::SourceClass1>();

    // 成功する場合
    bool is_timeout;
    if( subfunc_obj->WaitForRunning([](Timeout::SourceClass1* source, bool &is_done) {
        is_done = true;
        return true;
    }, std::chrono::milliseconds(3000), is_timeout) ) {
        std::cout << "Success" << std::endl;
    } else {
        std::cout << "Failed" << std::endl;
        if( is_timeout) {
            std::cout << "Timeout" << std::endl;
        }
    }

    // 失敗する場合
    if (subfunc_obj->WaitForRunning([](Timeout::SourceClass1* source, bool& is_done) {
        is_done = true;
        return false;
    }, std::chrono::milliseconds(3000), is_timeout)) {
        std::cout << "Success" << std::endl;
    } else {
        std::cout << "Failed" << std::endl;
        if (is_timeout) {
            std::cout << "Timeout" << std::endl;
        }
    }

    // タイムアウト
    if (subfunc_obj->WaitForRunning([](Timeout::SourceClass1* source, bool& is_done) {
        Sleep(4000);            // ** Windows.h固有コード. gccならばsys/time.hのsleep ** //
        return true;
    }, std::chrono::milliseconds(3000), is_timeout)) {
        std::cout << "Success" << std::endl;
    } else {
        std::cout << "Failed" << std::endl;
        if (is_timeout) {
            std::cout << "Timeout" << std::endl;
        }
    }


    return 0;
}

結果

Success
Failed
Failed
Timeout
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1