Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
sanjusss opened this issue Dec 10, 2020 · 0 comments
Open

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

sanjusss opened this issue Dec 10, 2020 · 0 comments
Labels

Comments

@sanjusss
Copy link

核心思想就是为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;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants