Make up the called object

I need a template function:

template <typename ...Signatures, typename ...Functions> auto make_callable_object (Functions&& ...functions); 

which will return the called object. When the called object is called with any signature from the signature, the corresponding functions from the functions should be called.

eg:.

 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"; } ); 

should return an object equivalent

 struct { void operator() (int) const { std::cout << "int"; } void operator() (std::string) const { std::cout << "string"; } void operator() (std::exception) const { std::cout << "exception"; } }; 

I created some implementation but it does not compile with clang C ++ 11.

I am not sure if there is an error or a compiler error in my code. I am looking for workarounds, or maybe the best solution to be compiled with both gcc and clang in C ++ 11 mode.

Link Coliru

 #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"; } } 

UPDATE:

I tried another solution and got rid of explicit signatures in the make_callable template parameters. So now the link function is called in this simple way:

 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"; } } 

And here is the Coliru Demo . But I'm still not sure if it is efficient enough or perhaps a much better solution.

+5
source share
1 answer

The following approach is shorter (and IMHO is easier to clutter up):

You did a search to determine if the called code matches:

 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...> { }; 

Allows you to define callbacks so that we can consider an undetected case that is identical to everything else:

 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 {}; } }; 

Now, given the tuple of called elements, let's calculate the index of the first matching name:

 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; }; 

The callable pattern can be a completely rollback agnostic, which can be quite convenient, in fact, if one day you want to have different behavior (for example, another exception). You just need to call the first corresponding element of the tuple:

 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)...); } }; 

The factory functions will finally add a backup.

 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>{})}; } 

The code is also available here: http://coliru.stacked-crooked.com/a/3942dfd3de7c1ef8

+2
source

Source: https://habr.com/ru/post/1242577/


All Articles