C++ 组合可调用对象

C++ 组合可调用对象,c++,c++11,C++,C++11,我需要一个模板函数: template <typename ...Signatures, typename ...Functions> auto make_callable_object (Functions&& ...functions); 模板 自动生成可调用对象(函数和函数); 这将返回一个可调用对象。当使用来自签名的任何签名调用该可调用对象时,应该调用来自函数的相应函数 例如: auto-co=create\u callable\u对象( [](int i

我需要一个模板函数:

template <typename ...Signatures, typename ...Functions>
auto make_callable_object (Functions&& ...functions);
模板
自动生成可调用对象(函数和函数);
这将返回一个可调用对象。当使用来自签名的任何签名调用该可调用对象时,应该调用来自函数的相应函数

例如:

auto-co=create\u callable\u对象(

[](int i){std::cout以下方法较短(且更易于探索):

您做了大量工作来检测可调用项是否匹配:

struct can_call_test
{
  template<typename F, typename... A>
  static decltype(std::declval<F>()(std::declval<A>()...), std::true_type())
  f(int);

  template<typename F, typename... A>
  static std::false_type
  f(...);
};

} // namespace detail

template<typename F, typename... A>
struct is_callable : decltype(detail::can_call_test::f<F, A...>(0)) { };

template<typename F, typename... A>
struct is_callable <F(A...)> : is_callable <F, A...> { };
工厂函数最终添加了回退

template <class... Functions>
callable<typename std::decay<Functions>::type..., Fallback<true>>
make_strict_callable(Functions&&... functions)
{
  return {std::forward_as_tuple(std::forward<Functions>(functions)...,
                                Fallback<true>{})};
}

template <class... Functions>
callable<typename std::decay<Functions>::type..., Fallback<false>>
make_callable(Functions&&... functions)
{
  return {std::forward_as_tuple(std::forward<Functions>(functions)...,
                                Fallback<false>{})};
}
模板
可调用
使\u严格\u可调用(函数和函数)
{
返回{std::forward_作为_元组(std::forward(函数)。。。,
后备{}};
}
模板
可调用
使_可调用(函数&&…函数)
{
返回{std::forward_作为_元组(std::forward(函数)。。。,
后备{}};
}
代码也张贴在这里:

值得一提的是,您的代码似乎可以与and一起工作,但不会发出叮当声。出于好奇:您为什么会使用非严格版本?我始终希望编译器告诉我签名不正确。感谢您提供的酷解决方案。我喜欢您插入回退的方式。(直接)你计算指数的方法也很酷。我需要“让你可以调用”函数创建一个智能访问者来检查或修改解析器和解释器中的boost::variant类型。我需要在运行时处理那里的问题,所以我使用非严格的可调用函数。我创建了一个演示如何使用它。但有趣的是,您的make_可调用实现的演示无法使用clang编译(gcc可以)。你能看一下吗?我用可变模板替换了c样式的省略号(并且我纠正了
static_assert
中的条件以实际触发)。我用你的变体代码尝试了新的回退:下面是一个很好的答案,解释为什么省略号方式只适用于某些编译器:
#include <iostream>
#include <tuple>
#include <type_traits>
#include <functional>

#define noexcept
#define constexpr
#define constexpr14

struct callable_impl_base {
  // will trigger if called with bad signature
  template <typename ...Ts>
  void operator() (Ts...) const { throw std::bad_function_call {}; }
};

template <typename Func, typename Base, typename Sig>
struct callable_impl;

template <typename Func, typename Base, typename R, typename ...Args>
struct callable_impl<Func, Base, R (Args...)>: public Base
{
  template <typename FF>
  constexpr callable_impl (FF&& f, Base&& b)
    : Base (std::forward<Base> (b))
    , func (std::forward<FF> (f))
  {
  }

  // unhiding method from the base classes.
  using Base::operator();

  constexpr R operator() (Args&& ...args) const
  {
    return func (std::forward<Args> (args)...);
  }

  constexpr14 R operator() (Args&& ...args)
  {
    return func (std::forward<Args> (args)...);
  }

  Func func;
};

template <typename Sig, typename Func, typename Base>
constexpr
callable_impl<
    typename std::decay<Func>::type
  , typename std::decay<Base>::type
  , Sig
>
make_callable_impl (Func&& func, Base&& base)
{
  return { std::forward<Func> (func), std::forward<Base> (base) };
}

// Recursion stopper.
template <typename ...>
constexpr callable_impl_base 
make_callable () { return {}; }

// Strip first Sig and first Func one by one.
template <typename Sig, typename ...Sigs, typename F, typename ...Fs>
constexpr14 auto
make_callable (F&& f, Fs&& ...fs)
   -> decltype (make_callable_impl<Sig> (
        std::forward<F> (f),
        make_callable<Sigs...> (std::forward<Fs> (fs)...)))
{
  static_assert (sizeof... (Sigs) == sizeof... (Fs), "bad number of args");

  return make_callable_impl<Sig> (
      std::forward<F> (f),
      make_callable<Sigs...> (std::forward<Fs> (fs)...));
}

using namespace std;

struct A {};
struct B {};

int main ()
{
  auto x = make_callable<void (const A&), void(B const&), void(int,int,int)> (
                [] (A const&) {cout << "A\n";},
                [] (B const&) {cout << "B\n";},
                [] (int,int,int) { cout << "int,int,int\n"; }
  );

  x (B{});
  x (A{});
  x (1,2,4);

  // this must throw because of incompatible signature.
  try {
    x (1,2);
  }
  catch (std::bad_function_call)
  {
      std::cout << "x (1,2) -> got exception (ok)\n";
  }
}
int main ()
{
  using namespace std;

  auto co = make_callable (
       [] (int) { cout << "int\n"; },
       [] (string) { cout << "string\n"; },
       [] (int,int) { cout << "int,int\n"; }
  );

  cout << "co(\"str\") -> "; co ("fff");
  cout << "co(55) -> "; co (55);
  cout << "co(55, 44) -> "; co (55, 44);

  // This must throw exception.
  try { co ('c', 4, 4); }
  catch (bad_function_call) { cout << "co('c',4,4) -> exception (ok)\n"; }

}
struct can_call_test
{
  template<typename F, typename... A>
  static decltype(std::declval<F>()(std::declval<A>()...), std::true_type())
  f(int);

  template<typename F, typename... A>
  static std::false_type
  f(...);
};

} // namespace detail

template<typename F, typename... A>
struct is_callable : decltype(detail::can_call_test::f<F, A...>(0)) { };

template<typename F, typename... A>
struct is_callable <F(A...)> : is_callable <F, A...> { };
template <bool Strict>
struct Fallback
{
  template<typename... Args, typename T = int>
  void operator()(Args&&...) const
  {
    static_assert (sizeof(T) == 0,
      "Bad function call: incompatible signature, see next error message");
  }
};

template <>
struct Fallback<false>
{
  template<typename... Args>
  void operator()(Args&&...) const
  {
    throw std::bad_function_call {};
  }
};
template <size_t Idx, typename Tuple, typename... Args>
struct FirstCallable;

template <size_t Idx, typename C, typename... Rest, typename... Args>
struct FirstCallable<Idx, std::tuple<C, Rest...>, Args...>
{
  static constexpr size_t index =
      is_callable<C, Args...>::value
          ? Idx
          : FirstCallable<Idx + 1, std::tuple<Rest...>, Args...>::index;
};

template <size_t Idx, typename C, typename... Args>
struct FirstCallable<Idx, std::tuple<C>, Args...>
{
  static constexpr size_t index = Idx;
};
template <class... Functions>
struct callable
{
  using FTuple = std::tuple<Functions...>;

  FTuple functions;

  template <typename Tuple>
  callable(Tuple&& f)
      : functions(std::forward<Tuple>(f))
  {
  }

  template <class... Args>
  auto operator()(Args&&... args) const
      -> decltype(std::get<FirstCallable<0, FTuple, Args...>::index>(functions)(
          std::forward<Args>(args)...))
  {
    return std::get<FirstCallable<0, FTuple, Args...>::index>(functions)(
        std::forward<Args>(args)...);
  }
};
template <class... Functions>
callable<typename std::decay<Functions>::type..., Fallback<true>>
make_strict_callable(Functions&&... functions)
{
  return {std::forward_as_tuple(std::forward<Functions>(functions)...,
                                Fallback<true>{})};
}

template <class... Functions>
callable<typename std::decay<Functions>::type..., Fallback<false>>
make_callable(Functions&&... functions)
{
  return {std::forward_as_tuple(std::forward<Functions>(functions)...,
                                Fallback<false>{})};
}