3
\$\begingroup\$

Sometimes you need to do things when a condition changes, here is a simple class to keep track of the state of the last call and execute callbacks when the state changes.


#include <functional>
#include <cassert>


/// latch is a class that contains of a test function and a couple of callbacks, when the return
/// value of the test function changes the approrpiate callback gets called, as long as the return
/// value of the test function stays the same no callbacks will be called. Callbacks may be nullptr
template<class...Args>
class Latch {
public:
    Latch(std::function<bool(Args ...)> shouldLatch, std::function<void(Args...)> onLatch = nullptr, std::function<void(Args...)> onUnlatch = nullptr) :
                m_shouldLatch(std::move(shouldLatch)),
                m_onLatch(std::move(onLatch)),
                m_onUnlatch(std::move(onUnlatch)) {}

    bool latched() const noexcept { return m_isLatched; }
    
    explicit operator bool() const noexcept { return m_isLatched; }

    void update(Args&& ... args)
    {
        if (m_shouldLatch && m_isLatched != m_shouldLatch(std::forward<Args>(args)... ))
        {
            m_isLatched = !m_isLatched;
            auto& call = (m_isLatched) ?  m_onLatch : m_onUnlatch;
            if (call) call(std::forward<Args>(args)...);
        }
    }


private:
    bool m_isLatched = false;
    std::function<bool(Args ...)> m_shouldLatch;
    std::function<void(Args ...)> m_onLatch;
    std::function<void(Args ...)> m_onUnlatch;
};

int main() 
{
    int onLatch = 0;
    int onUnlatch = 0;
    int latchVal = 1;


    Latch<int> l1([&latchVal](auto val){return val == latchVal;},
                  [&onLatch](auto val){++onLatch;},
                  [&onUnlatch](auto val){++onUnlatch;});
    assert(!l1);
    l1.update(0);
    assert(!l1);
    assert(onLatch == 0 && onUnlatch == 0);
    l1.update(1);
    assert(l1.latched());   
    assert(onLatch == 1 && onUnlatch == 0);
    l1.update(1);
    assert(onLatch == 1 && onUnlatch == 0);
    l1.update(0);
    assert(onLatch == 1 && onUnlatch == 1);

    Latch l2 = l1;
}

This is also at https://www.godbolt.org/z/z4b934

Thanks for the input

\$\endgroup\$
2
  • 2
    \$\begingroup\$ What are you looking to improve? \$\endgroup\$ – mkamerath Oct 23 '20 at 16:18
  • \$\begingroup\$ Wasn’t looking for any specifics, just a general review. checking the use of variable template arguments and forwarding for example. \$\endgroup\$ – Harald Scheirich Oct 24 '20 at 21:59
3
\$\begingroup\$

There is not much to improve in my opinion, except:

Unnecessary use of std::move()

You are passing shouldLatch, onLatch and onUnlatch by value to the constructor, so there is no reason to use std::move() in the member initializer list.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.