~aleteoryx/muditaos

08e102303d90320d8f007e83353a58c9514ec09d — Piotr Tański 4 years ago b966135
[EGD-5697] Add helper functions for phone modes

They make it easier to write a code dependent on phone modes.
M enabled_unittests => enabled_unittests +9 -0
@@ 254,6 254,15 @@ TESTS_LIST["catch2-utils-clipboard"]="
    Clipboard;
"
#---------
TESTS_LIST["catch2-utils-conditional-invoke-tests"]="
    Successful global function call;
    Failed global function call;
    Successful class static function call;
    Failed class static function call;
    Successful class member function call;
    Failed class member function call;
"
#---------
TESTS_LIST["catch2-utils-duration"]="
    Duration - creation;
    Duration - arithemtics;

M module-utils/test/CMakeLists.txt => module-utils/test/CMakeLists.txt +9 -0
@@ 1,5 1,14 @@
cmake_minimum_required(VERSION 3.12)

add_catch2_executable(
    NAME
        utils-conditional-invoke-tests
    SRCS
        test-Utility-ConditionalInvoke.cpp
    LIBS
        module-utils
)

# Phone number tests
add_catch2_executable(
    NAME

A module-utils/test/test-Utility-ConditionalInvoke.cpp => module-utils/test/test-Utility-ConditionalInvoke.cpp +203 -0
@@ 0,0 1,203 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>

#include <module-utils/utility/Utility.hpp>

struct SomeStruct
{
    int value;
};

class ExampleClass
{
  public:
    int function1(int x, const SomeStruct &s)
    {
        return x + s.value;
    }

    void function2()
    {}

    int function3(int x, SomeStruct &s)
    {
        return x + s.value;
    }

    static int static_function1(int x, const SomeStruct &s)
    {
        return x + s.value;
    }

    static void static_function2()
    {}

    static int static_function3(int x, SomeStruct &s)
    {
        return x + s.value;
    }
};

int global_function1(int x, const SomeStruct &s)
{
    return x + s.value;
}

void global_function2()
{}

int global_function3(int x, SomeStruct &s)
{
    return x + s.value;
}

TEST_CASE("Successful global function call")
{
    const auto guard = []() { return true; };

    const auto [called1, ret1] = utility::conditionally_invoke(guard, &global_function1, 10, SomeStruct{5});
    REQUIRE(called1 == true);
    REQUIRE(ret1 == 15);

    int x = 10;
    SomeStruct s1{10};
    const auto [called2, ret2] = utility::conditionally_invoke(guard, &global_function1, x, s1);
    REQUIRE(called2 == true);
    REQUIRE(ret2 == 20);

    const auto called3 = utility::conditionally_invoke(guard, &global_function2);
    REQUIRE(called3 == true);

    SomeStruct s2{15};
    const auto [called4, ret4] = utility::conditionally_invoke(guard, &global_function3, 10, std::ref(s2));
    REQUIRE(called4 == true);
    REQUIRE(ret4 == 25);
}

TEST_CASE("Failed global function call")
{
    const auto guard = []() { return false; };

    const auto [called1, ret1] = utility::conditionally_invoke(guard, &global_function1, 10, SomeStruct{5});
    REQUIRE(called1 == false);
    REQUIRE(ret1 == 0);

    int x = 10;
    SomeStruct s1{10};
    const auto [called2, ret2] = utility::conditionally_invoke(guard, &global_function1, x, s1);
    REQUIRE(called2 == false);
    REQUIRE(ret2 == 0);

    const auto called3 = utility::conditionally_invoke(guard, &global_function2);
    REQUIRE(called3 == false);

    SomeStruct s2{15};
    const auto [called4, ret4] = utility::conditionally_invoke(guard, &global_function3, 10, std::ref(s2));
    REQUIRE(called4 == false);
    REQUIRE(ret4 == 0);
}

TEST_CASE("Successful class static function call")
{
    const auto guard = []() { return true; };

    const auto [called1, ret1] =
        utility::conditionally_invoke(guard, &ExampleClass::static_function1, 10, SomeStruct{5});
    REQUIRE(called1 == true);
    REQUIRE(ret1 == 15);

    int x = 10;
    SomeStruct s1{10};
    const auto [called2, ret2] = utility::conditionally_invoke(guard, &ExampleClass::static_function1, x, s1);
    REQUIRE(called2 == true);
    REQUIRE(ret2 == 20);

    const auto called3 = utility::conditionally_invoke(guard, &ExampleClass::static_function2);
    REQUIRE(called3 == true);

    SomeStruct s2{15};
    const auto [called4, ret4] =
        utility::conditionally_invoke(guard, &ExampleClass::static_function3, 10, std::ref(s2));
    REQUIRE(called4 == true);
    REQUIRE(ret4 == 25);
}

TEST_CASE("Failed class static function call")
{
    const auto guard = []() { return false; };

    const auto [called1, ret1] =
        utility::conditionally_invoke(guard, &ExampleClass::static_function1, 10, SomeStruct{5});
    REQUIRE(called1 == false);
    REQUIRE(ret1 == 0);

    int x = 10;
    SomeStruct s1{10};
    const auto [called2, ret2] = utility::conditionally_invoke(guard, &ExampleClass::static_function1, x, s1);
    REQUIRE(called2 == false);
    REQUIRE(ret2 == 0);

    const auto called3 = utility::conditionally_invoke(guard, &ExampleClass::static_function2);
    REQUIRE(called3 == false);

    SomeStruct s2{15};
    const auto [called4, ret4] =
        utility::conditionally_invoke(guard, &ExampleClass::static_function3, 10, std::ref(s2));
    REQUIRE(called4 == false);
    REQUIRE(ret4 == 0);
}

TEST_CASE("Successful class member function call")
{
    ExampleClass instance;
    const auto guard = []() { return true; };

    const auto [called1, ret1] =
        utility::conditionally_invoke(guard, instance, &ExampleClass::function1, 10, SomeStruct{5});
    REQUIRE(called1 == true);
    REQUIRE(ret1 == 15);

    int x = 10;
    SomeStruct s1{10};
    const auto [called2, ret2] = utility::conditionally_invoke(guard, instance, &ExampleClass::function1, x, s1);
    REQUIRE(called2 == true);
    REQUIRE(ret2 == 20);

    const auto called3 = utility::conditionally_invoke(guard, instance, &ExampleClass::function2);
    REQUIRE(called3 == true);

    SomeStruct s2{15};
    const auto [called4, ret4] =
        utility::conditionally_invoke(guard, instance, &ExampleClass::function3, 10, std::ref(s2));
    REQUIRE(called4 == true);
    REQUIRE(ret4 == 25);
}

TEST_CASE("Failed class member function call")
{
    ExampleClass instance;
    const auto guard = []() { return false; };

    const auto [called1, ret1] =
        utility::conditionally_invoke(guard, instance, &ExampleClass::function1, 10, SomeStruct{5});
    REQUIRE(called1 == false);
    REQUIRE(ret1 == 0);

    int x = 10;
    SomeStruct s1{10};
    const auto [called2, ret2] = utility::conditionally_invoke(guard, instance, &ExampleClass::function1, x, s1);
    REQUIRE(called2 == false);
    REQUIRE(ret2 == 0);

    const auto called3 = utility::conditionally_invoke(guard, instance, &ExampleClass::function2);
    REQUIRE(called3 == false);

    SomeStruct s2{15};
    const auto [called4, ret4] =
        utility::conditionally_invoke(guard, instance, &ExampleClass::function3, 10, std::ref(s2));
    REQUIRE(called4 == false);
    REQUIRE(ret4 == 0);
}

A module-utils/utility/Utility.hpp => module-utils/utility/Utility.hpp +87 -0
@@ 0,0 1,87 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <functional>
#include <utility>
#include <type_traits>

namespace utility
{
    using Guard = std::function<bool()>;

    /// Invokes a function if specified conditions are met.
    /// \tparam F       Function signature
    /// \tparam Args    Function arguments
    /// \param guard    Conditions to be met
    /// \param func     Callable object
    /// \param args     Function arguments
    /// \return If the function returns void - a flag indicating whether the conditions were met and the function has
    /// been called. If the function returns a type - a pair of: a flag indicating whether the conditions were met, and
    /// a function's return value.
    template <typename F, typename... Args>
    auto conditionally_invoke(const Guard &guard,
                              const F &func,
                              Args... args) noexcept(std::is_nothrow_invocable_v<F, Args...>)
    {
        using ResultType              = typename std::invoke_result_t<F, Args...>;
        constexpr auto isVoidFunction = std::is_void_v<ResultType>;
        if (!guard()) {
            if constexpr (isVoidFunction) {
                return false;
            }
            else {
                return std::pair(false, ResultType{});
            }
        }

        if constexpr (isVoidFunction) {
            std::invoke(func, std::forward<Args>(args)...);
            return true;
        }
        else {
            return std::pair(true, std::invoke(func, std::forward<Args>(args)...));
        }
    }

    /// Invokes a member function if specified conditions are met.
    /// \tparam T       Class which contains the member function F
    /// \tparam F       Function signature
    /// \tparam Args    Function arguments
    /// \param guard    Conditions to be met
    /// \param self     Pointer to the class T instance
    /// \param func     Callable object
    /// \param args     Function arguments
    /// \return If the function returns void - a flag indicating whether the conditions were met and the function has
    /// been called. If the function returns a type - a pair of: a flag indicating whether the conditions were met, and
    /// a function's return value.
    template <class T,
              typename F,
              typename... Args,
              typename std::enable_if_t<std::is_invocable_v<F, T, Args...>, bool> = false>
    auto conditionally_invoke(const Guard &guard,
                              T &&self,
                              const F &func,
                              Args... args) noexcept(std::is_nothrow_invocable_v<F, T, Args...>)
    {
        using ResultType              = typename std::invoke_result_t<F, T, Args...>;
        constexpr auto isVoidFunction = std::is_void_v<ResultType>;
        if (!guard()) {
            if constexpr (isVoidFunction) {
                return false;
            }
            else {
                return std::pair(false, ResultType{});
            }
        }

        if constexpr (isVoidFunction) {
            std::invoke(func, std::forward<T>(self), std::forward<Args>(args)...);
            return true;
        }
        else {
            return std::pair(true, std::invoke(func, std::forward<T>(self), std::forward<Args>(args)...));
        }
    }
} // namespace utility