我需要一个模板功能:
template <typename ...Signatures, typename ...Functions>
auto make_callable_object (Functions&& ...functions);
将返回一个可调用对象。当使用 Signatures 中的任何签名调用该可调用对象时,应调用 Functions 中的对应函数。
E.g:
auto co = create_callable_object<void (int), void (std::string), void (std::exception)> (
[] (int i) { std::cout << "int"; },
[] (std::string) { std::cout << "string"; }
[] (std::exception) { std::cout << "exception"; }
);
应该返回一个等同于
的对象struct {
void operator() (int) const { std::cout << "int"; }
void operator() (std::string) const { std::cout << "string"; }
void operator() (std::exception) const { std::cout << "exception"; }
};
我创建了一些实现,但它不能用clang c ++ 11编译。
我不确定我的代码中是否存在编译器错误或错误。我正在寻找解决方法,或者可能是一个更好的解决方案,可以在c ++ 11模式下使用gcc和clang进行编译。
#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";
}
}
更新
我尝试了另一种解决方案并删除 make_callable 模板参数中的显式签名。因此,现在以如此简单的方式调用compose函数:
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"; }
}
这是Coliru Demo。但我仍然不确定它是否非常有效,或者可能有更好的解决方案。
答案 0 :(得分:2)
以下方法更短(并且恕我直言):
你做了很多工作来检测一个callable是否匹配:
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...> { };
让我们定义后备callables,以便我们可以将 not found 案例视为与所有其他案例相同:
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;
};
callable
模板可以完全与后备无关,这可能非常方便,实际上,如果有一天你想要有不同的行为(例如不同的例外)。它只需要调用第一个匹配的元组元素:
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>{})};
}
代码也贴在这里: http://coliru.stacked-crooked.com/a/3942dfd3de7c1ef8