Lambda回调取决于其参数

时间:2015-08-29 18:23:11

标签: c++ lambda template-meta-programming

我正在编写一个库,用户将回调作为lambda提供。在默认情况下,我想只调用lambda并传回一个对象。

现在有一些非常普通的情形,用户也可能想要上下文。所以我希望能够使用相同的回调机制,只允许用户将上下文作为参数添加到他们的lambda中,然后我将传递对象和上下文。

我无法让SFINAE工作。

我已将代码简化为:

#include <string>
#include <iostream>

class Context {};


template<typename F>
struct UseContext
{
    // I want to set this value to 0 or 1 based on the parameters
    // in F but can't quite get this to work.
    enum {value = 0 };
};


template<typename F, typename T, bool useContext = UseContext<F>::value>
struct Caller;

template<typename F, typename T>
struct Caller<F, T, true>
{
    void operator()(F& func, Context& context, T& object)
    {
        func(context, object);
    }
};
template<typename F, typename T>
struct Caller<F, T, false>
{
    void operator()(F& func, Context&, T& object)
    {
        func(object);
    }
};



template<typename T, typename F>
void doWork(F&& func)
{
  Context  context;
  T        object;

  /// STUFF
  Caller<F,T>  caller;
  caller(func, context, object);
}

用法:

int main()
{
    // if UseContext::value == 0 then this compiles.
    // This is the normal situation.
    doWork<std::string>([](std::string const& x){ std::cout << x << "\n";});

    // if UseContext::value == 1 then this compiles.
    // This is if the user wants more context about the work.
    // most of the time this extra parameter is not required.
    // So I don't want to force the user to add it to the parameter
    // list of the lambda.
    doWork<std::string>([](Context&, std::string const& x){ std::cout << x << "\n";});
}

或者,如果有更好的方法可以做到这一点。

2 个答案:

答案 0 :(得分:5)

表达SFINAE:

public byte[] consume(InputStream in) throws Exception {
  ByteArrayOutputStream out = new ByteArrayOutputStream();
  byte[] buffer = new byte[2048];
  int read = -1;
  while ((read = in.read(buffer)) > 0) {
    out.write(buffer, 0, read);
  }
  return out.toByteArray();
}

然后只是template<class F, class T> auto call(F& func, Context& context, T& object) -> decltype(func(context, object), void()) { func(context, object); } template<class F, class T> auto call(F& func, Context&, T& object) -> decltype(func(object), void()) { func(object); } 。如果两种形式都有效,则这是不明确的。如果你想消除歧义,只需添加一个虚拟参数并执行通常的call(func, context, object) / int技巧。

答案 1 :(得分:1)

我的解决方案是使用std::is_constructiblestd::enable_if

template<typename F,typename T>
typename std::enable_if<std::is_constructible<std::function<void(T const&)>,F>::value>:type doWork(F func)
{
//...
}

template<typename F,typename T>
typename std::enable_if<std::is_constructible<std::function<void(Context&,T const&)>,F>::value>:type doWork(F func)
{
//...
}

explenation - 每个std::function都可以从等效的lambda中构建。这里我们正在使用std::enable_if进行测试,如果您可以构建std::function<void(T)>std::function<void(Context,T)>并在编译时重新连接正确的函数。