C++ 中的去抖动

问题描述 投票:0回答:3

我正在尝试用 C++ 编写一个虚拟去抖动函数。这是我写的:

 #include <bits/stdc++.h>
    using namespace std;
    using namespace chrono;
    function<void(int)>debounce(function<void(void)>&f , int period){
        function<void(int)> fn = [&](int per){
            static auto init_time = high_resolution_clock::now();
            auto final_time = high_resolution_clock::now();
            if ( duration_cast<milliseconds>(final_time - init_time).count() > per){
                f();
            }
            init_time = final_time;
        };
        return fn;
    }
    int main(void){
        int x = 0;
        function<void(void) > f = [&x](void){
            x++;
        };
        function<void(int)> xdf = debounce(f , 30);
        std::this_thread::sleep_for(milliseconds(300));
        xdf(300);
        xdf(300);
        std::this_thread::sleep_for(milliseconds(300));
        xdf(300);
        if(x>=3 || x != 2){
            cout << "Debounce failed" << '\n'; 
        }else {
            cout << "Successful" << '\n'; 
        }
        return 0;
    }

但这不太好用。有什么方法可以让我不必将时间限制传递给 xdf 函数吗?也许这不是正确的代码。你能给出一些其他的想法来实施或纠正这个想法吗?

c++ c++17 debouncing
3个回答
2
投票

您链接的 go 实现的单词到单词的转换。

#include <bits/stdc++.h>
#include <stdexcept>
using namespace std;
using namespace chrono;

function<void()> Debounced(function<void()>&f , int period){
    static auto created = high_resolution_clock::now();
    // "=" allow to pass by copy all used variables (created and period)
    // "&f" allow to pass by reference f variable
    function<void()> fn = [=,&f](){
        auto now = high_resolution_clock::now();
        if (duration_cast<milliseconds>(now - created).count() > period){
            f();
        }
    };
    return fn;
}

int main(void){
    int x = 0;
    function<void()> f = [&x](){
        x++;
    };

    auto dbf = Debounced(f, 500);
    for (int i = 0; i < 10; ++i) {
      dbf();
    }
    if (x != 0) {
      throw std::runtime_error("Expected x not to change since it's debounced for 500ms");
    }
    std::this_thread::sleep_for(milliseconds(500));
    for (int i = 0; i < 10; ++i) {
      dbf();
    }
    if (x != 10) {
      throw std::runtime_error("Expected x to be incremented 10 times");
    }
}

1
投票

我不久前制作了一个,基于 Qt C++(使用 QSharedPointer 和 QTimer),但如果您喜欢的话,您可以将其更改为基于 boost 的计时器和普通共享指针。

为了完整起见,我将代码粘贴在这里,您应该能够从注释中掌握它:

#ifndef QFUNCTIONUTILS_H
#define QFUNCTIONUTILS_H

#include <functional>
#include <QSharedPointer>
#include <QTimer>

namespace QFunctionUtils
{

// function used to cache user arguments to be able to use them once timer times out
template<typename R, class ...Args>
void debounce_args_cache(const std::function<R(Args(...args))> &funcFinish, Args(&...args))
{
    funcFinish(args...);
}

typedef QSharedPointer<QMetaObject::Connection> QMetaConnPtr;
// debounce implementation
// gets a user defined function as an argument and a 'delay' in miliseconds
// returns a new function that the user can call many times, but will only execute the last
// call after 'delay' miliseconds have passed
template<typename R, class ...Args>
std::function<R(Args(...args))> debounce_internal(const std::function<R(Args(...args))>     &callback, const int &delay)
{
    QTimer * timer = new QTimer;
    QMetaConnPtr conn = QMetaConnPtr(new QMetaObject::Connection,
        [timer](QMetaObject::Connection * pConn) mutable {
        // connection will be deleted when returned function goes out of scope, but not the timer
        // so we need to delete it manually
        delete pConn;
        delete timer;
    });
    timer->setInterval(delay);
    // return function that the user can call
    return [callback, timer, conn](Args(...args)) {
        // disconnect old timer callback, stop timer
        QObject::disconnect(*conn);
        timer->stop();
        // create function that caches user arguments
        std::function<void(std::function<R(Args(...args))>)> funcCache = std::bind(    debounce_args_cache<R, Args...>, std::placeholders::_1, args...);
        // connect new timer callback, restart timer
        *conn = QObject::connect(timer, &QTimer::timeout,
            [callback, timer, funcCache]() {
            // stop timer
            timer->stop();
            // call after delay
            funcCache(callback);
        });
        timer->start();
        // return default constructed return type (just to support any return type)
        return R();
    };
}

// throttle implementation
// same as debounce, but also calls the given function the first time is called
template<typename R, class ...Args>
std::function<R(Args(...args))> throttle_internal(const std::function<R(Args(...args))>     &callback, const int &delay)
{
    QTimer * timer = new QTimer;
    QMetaConnPtr conn = QMetaConnPtr(new QMetaObject::Connection,
        [timer](QMetaObject::Connection * pConn) mutable {
        // same logic as in debounce_internal
        delete pConn;
        delete timer;
    });
    timer->setInterval(delay);
    // return function that the user can call
    return [callback, timer, conn](Args(...args)) {
        // disconnect old timer callback
        QObject::disconnect(*conn);
        // check timer (throttle) state
        if (!timer->isActive())
        {
            // connect new timer callback
            *conn = QObject::connect(timer, &QTimer::timeout,
                [timer]() {
                // stop timer only (this happens if user did not call the function during the     delay)
                timer->stop();
            });
            // start timer to start throttle
            timer->start();
            // call inmediatly and return
            return callback(args...);
        }
        // create function that caches user arguments
        std::function<void(std::function<R(Args(...args))>)> funcCache = std::bind(    debounce_args_cache<R, Args...>, std::placeholders::_1, args...);
        // connect new timer callback
        *conn = QObject::connect(timer, &QTimer::timeout,
            [callback, timer, funcCache]() {
            // stop timer
            timer->stop();
            // call after delay
            funcCache(callback);
        });
        // return default constructed return type (just to support any return type)
        return R();
    };
}

// for any class implementing operator() (e.g. lambdas)
template<typename T> struct functor_traits : functor_traits<decltype(&T::operator())>
{ };
// specialization - lambda
template<typename C, typename R, typename... Args>
struct functor_traits<R(C::*)(Args...) const>
{
    using lambda_type = std::function<R(Args...)>;
};
// specialization - lambda mutable
template<typename C, typename R, typename... Args>
struct functor_traits<R(C::*)(Args...)>
{
    using lambda_type = std::function<R(Args...)>;
};
// specialization - function pointer
template<class R, class... Args>
struct functor_traits<R(*)(Args...)>
{
    using lambda_type = std::function<R(Args...)>;
};
// wrap any of the specializations above into an std::function
template<typename T> 
auto make_std_function(const T& fn) -> typename functor_traits<T>::lambda_type
{
    return typename functor_traits<T>::lambda_type(fn);
}
// convert callback into std::function and call internal debounce
template<typename T>
auto Debounce(T callback, const int &delay) -> typename functor_traits<T>::lambda_type
{
    return debounce_internal(make_std_function(callback), delay);
}
// convert callback into std::function and call internal throttle
template<typename T>
auto Throttle(T callback, const int &delay) -> typename functor_traits<T>::lambda_type
{
    return throttle_internal(make_std_function(callback), delay);
}

}

#endif // QFUNCTIONUTILS_H

0
投票

我也遇到过这个,并想扩展@Erwan的答案:

#include <chrono>
#include <functional>

class DebouncedAction 
{
public:
    explicit DebouncedAction(std::function<void()> callback, int period)
        : interval(std::chrono::milliseconds(period)),
        lastCallTime(),
        callback(std::move(callback)) {}

    void invoke()
    {
        auto now = std::chrono::high_resolution_clock::now();

        // Call immediately on the first invocation or on the next interval
        if (lastCallTime == std::chrono::high_resolution_clock::time_point() ||
            now - lastCallTime >= interval) 
        {
            callback();
            lastCallTime = now;
        }
    }

private:
    std::chrono::milliseconds interval;
    std::chrono::high_resolution_clock::time_point lastCallTime;
    std::function<void()> callback;
};
© www.soinside.com 2019 - 2024. All rights reserved.