首页 > 解决方案 > 如何转发未命名的函数参数

问题描述

所以,我正在尝试为我的虚拟类函数创建一个模拟宏。

这是具有一个模拟函数的模拟类的示例。(这按预期工作)

class MockEnvironment : public IEnvironment {
public:
    using runType = int(std::string program,
                        std::vector<std::string> arguments);

    MockedFunction<runType> runMock = MockedFunction<runType>("run");

    int run(std::string program, std::vector<std::string> arguments) override {
        return runMock(program, arguments);
    }

    //    MOCK_METHOD(int, run, (std::string, std::vector<std::string>)); // this is the goal
};

MockedFunction是一个带有 operator() 的类,它应该在函数被调用时被调用。

我的目标是将函数包装在一个宏中,以便能够模拟任何函数。您可能看到的问题是,在概括时,我需要处理一个带有未命名参数的函数。


#define MOCK_METHOD(ret, name, args)                                           \
    MockedFunction<ret(args)> mocked_##name =                                  \
        MockedFunction<ret(args)>(#name);                                      \
    ret name##Mock(args) { /* how to solve? */ }

// This would expand to something like
MockedFunction<...> runMock = MockedFunction<...>("run"); // More detailed in above example

int run(std::string, std::vector<std::string>) override {
    return runMock(/*how to forward the arguments when they do not have names?*/)
}

我曾想过使用某种可变参数模板,但我还没有找到任何方法让它工作。我也尝试阅读谷歌的模拟实现,但我发现很难看到他们是如何解决它的。

所以我的问题很简单。如何将参数的内容移动run到要调用的函数中?

我最多可以使用 c++17。我确实意识到,如果您在宏中指定需要多少个参数会更容易,但如果可以解决更一般的示例,那将是最佳选择。

编辑:如果我事先知道参数的数量,这是我想出的最好的方法。

#define INTERNAL_MOCK_METHOD_COMMON(ret, name, args)                           \
    using name##T = ret(args);                                                 \
    unittest::MockedFunction<ret(args)> mock_##name =                          \
        unittest::MockedFunction<ret(args)>(#name);

#define MOCK_METHOD(ret, name)                                                 \
    INTERNAL_MOCK_METHOD_COMMON(ret, name, ())                                 \
    ret name() {                                                               \
        return mock_##name();                                                  \
    }

#define MOCK_METHOD1(ret, name, args)                                          \
    INTERNAL_MOCK_METHOD_COMMON(ret, name, args)                               \
    ret name(typename unittest::MockedFunction<name##T>::ArgT<0>::type a) {    \
        return mock_##name(a);                                                 \
    }

#define MOCK_METHOD2(ret, name, args)                                          \
    INTERNAL_MOCK_METHOD_COMMON(ret, name, args)                               \
    ret name(typename unittest::MockedFunction<name##T>::ArgT<0>::type a,      \
             typename unittest::MockedFunction<name##T>::ArgT<1>::type b) {    \
        return mock_##name(a, b);                                              \
    }

// ...

template <typename T>
class MockedFunction {};

template <typename ReturnT, typename... ArgsT>
class MockedFunction<ReturnT(ArgsT...)> {
public:
    // Return argument type at the specified index
    template <int i>
    struct ArgT {
        using type = typename std::tuple_element_t<i, std::tuple<ArgsT...>>;
    };

那么就可以这样使用


class IObject {
public:
    virtual int update() = 0;
    virtual void setValue(int) = 0;
};

class MockObject : public IObject {
public:
    MOCK_METHOD(int, update);
    MOCK_METHOD1(void, setValue, (int));
};

明确一点:不必用数字指定参数的数量会很好。

标签: c++c++17

解决方案


您不能使用未命名的参数。你可以考虑给它们命名,可能是这样的:

#define MOCK_METHOD(ret, name, args, names)                                           \
    MockedFunction<ret(args)> mocked_##name =                                  \
        MockedFunction<ret(args)>(#name);                                      \
    ret name##Mock(args) { return runMock names; }

MOCK_METHOD(int, run, (std::string s, std::vector<std::string> v), (s, v));

推荐阅读