coolxv / cpp-stub

C++ unit test stub(not mock) and awesome.Surpported ISA x86,x86-64,arm64,arm32,arm thumb,mips64,riscv,loongarch64.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

使用lambda表达式作为桩函数的一种思路

sanjusss opened this issue · comments

核心**就是为lambda表达式生成独一无二的类和执行函数。

#include <cassert>
#include <utility>

#include "cpp_stub/stub.h"

/***
 * FakeFunction 可以封装一个函数或lambda表达式,用于替换目标函数。
 * 需要注意的是模板参数中的N和S,对于同一个测试用例中的FakeFunction,一定不能有相同的N、S组合。
 * 如果测试用例中只有一个FakeFunction或传入的函数声明都不相同时,可以省略N或S。
 * 如果想要通过某个类型区分FakeFunction,请设置模板参数S。
 *
 * 使用示例:
 *      auto fun = []() { prstd::size_tf("hehe\n"); };
 *      FakeFunction<void(void), 1> fl(fun);
 *
 *      decltype(fl)::Exec 即为lambda表达式的函数指针,可以用于Stub::set函数。
 */
template <class T, std::size_t N = 0, class S = void>
class FakeFunction;

template <class Res, class... Args, std::size_t N, class S>
class FakeFunction<Res(Args...), N, S> {
public:
    template <class Function>
    explicit FakeFunction(Function f)
        : m_Call(Call<Function>), m_Release(Release<Function>), m_Function(new Function(f)) {
        assert(m_Instance == nullptr);
        m_Instance = this;
    }

    template <class Method, class Function>
    explicit FakeFunction(Stub* stub, Method m, Function f) : FakeFunction(f) {
        stub->set(m, Exec);
    }

    template <class Method, class Function, class ExitFunction>
    explicit FakeFunction(Stub* stub, Method m, Function f, ExitFunction e) : FakeFunction(stub, m, f) {
        m_CallWhenExit = CallWhenExit<ExitFunction>;
        m_FunctionCallWhenExit = new ExitFunction(e);
    }

    ~FakeFunction() {
        if (m_CallWhenExit != nullptr) {
            m_CallWhenExit(this);
            m_CallWhenExit = nullptr;
        }

        m_Release(this);
        m_Release = nullptr;
        m_Instance = nullptr;
    }

    FakeFunction(const FakeFunction&) = delete;
    FakeFunction& operator=(const FakeFunction&) = delete;

    static Res Exec(Args... args) { return m_Instance->m_Call(m_Instance, std::forward<Args>(args)...); }

private:
    template <class Function>
    static Res Call(FakeFunction* that, Args... args) {
        ++(that->CallTimes);
        Function* f = static_cast<Function*>(that->m_Function);
        return (*f)(std::forward<Args>(args)...);
    }

    template <class Function>
    static void Release(FakeFunction* that) {
        delete static_cast<Function*>(that->m_Function);
    }

    template <class Function>
    static void CallWhenExit(FakeFunction* that) {
        (*static_cast<Function*>(that->m_FunctionCallWhenExit))(that->CallTimes);
        delete static_cast<Function*>(that->m_FunctionCallWhenExit);
    }

public:
    int CallTimes = 0;

private:
    static FakeFunction* m_Instance;
    Res (*m_Call)(FakeFunction*, Args...) = nullptr;
    void (*m_Release)(FakeFunction*) = nullptr;
    void (*m_CallWhenExit)(FakeFunction*) = nullptr;
    void* m_Function = nullptr;
    void* m_FunctionCallWhenExit = nullptr;
};

template <class Res, class... Args, std::size_t N, class S>
FakeFunction<Res(Args...), N, S>* FakeFunction<Res(Args...), N, S>::m_Instance = nullptr;