如何创建修改其参数的函数对象?

时间:2011-10-18 00:39:37

标签: c++ boost gmock

我想将SomeFunctionSetArg替换为来自boost的更通用的内容。 看起来可以使用bindlambda结合使用,但我不知道如何做。

此代码非常简单,但我想替换它的原因是因为我需要23等参数。

template<class T>
struct SomeFunction
{
    T value;
    SomeFunction(T s)
        : value(s) {}

    void operator()(T& s)
    {
        s = value;
    }
};

template<class T>
SomeFunction<T> SetArg(T value)
{
    return SomeFunction<T>(value);
}

要求:

  • 我想要一个返回函数对象的函数。
  • 当我调用此函数对象时,参数将通过引用传递。
  • 该函数通过将它们设置为预设值来修改通过引用传入的对象。
  • 在上面的代码中,预先设定的值会按ctor中的值传入,但任何其他方式也都可以。

以下代码演示了用法:

void main()
{
    std::string t;
    SetArg(std::string("hello"))(t);
    assert(t == "hello");
}

某些背景信息:

我想测试类Foo的客户端代码。所以我想用自己的func1替换struct Foo { virtual void func1(std::string& s) { } }; struct MockFoo : public Foo { MOCK_METHOD1(func1, void(std::string&)); }; void ExampleTestCase::example() { MockFoo f; std::string s; EXPECT_CALL(f, func1(_)) .WillOnce(Invoke(SetArg(std::string("hello")))); f.func1(s); CPPUNIT_ASSERT_EQUAL(std::string("hello"), s); } 的实现,但是以灵活的方式。

func1

Invoke接受一个函数或函数对象。在SetArg的新实现中,它调用"hello"返回的函数对象,并将其参数设置为字符串SetArg

Invoke是gmock / gtest的一部分,但{{1}}不是。

1 个答案:

答案 0 :(得分:1)

这是我想出的。可能是operator()的setter 需要一些调整,因为我们并没有真正受益于 可能会在这里移动语义,但我现在无法解决这个问题。

另请注意,这大量使用了可能没有的C ++ 11功能 可以使用。

#include <string>
#include <iostream>
#include <tuple>

// set arbitrary values to 
template<typename... Args>
struct setter {
  // needed because we cannot use initializer lists as they require assignment
  setter(const std::tuple<Args&...>& t) : t(t) {}
  std::tuple<Args...> t;

  // again a template to trigger deduction again
  template<typename... Args2>
  void operator()(Args2&&... args) {
    t = std::make_tuple(args...);
  }
};

template<typename... Args>
setter<Args&...> create_setter(Args&... args) {
  return setter<Args&...>(std::tie(args...));
}

int main()
{
  int i = 0;
  long l = 1;
  std::string foo = "foo";

  auto s = create_setter(i, l, foo);

  s(23, 42, "bar");
  std::cout << i << std::endl;
  std::cout << l << std::endl;
  std::cout << foo << std::endl;

  return 0;
}