Ich brauche eine Template-Funktion:Compose aufrufbare Objekt
template <typename ...Signatures, typename ...Functions>
auto make_callable_object (Functions&& ...functions);
, die ein aufrufbare Objekt zurück. Wenn dieses aufrufbare Objekt mit einer Signatur aus Signaturen aufgerufen wird, sollten die entsprechenden Funktionen von Funktionen aufgerufen werden.
Z. B .:
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"; }
);
sollte ein Objekt zurückgeben, die
struct {
void operator() (int) const { std::cout << "int"; }
void operator() (std::string) const { std::cout << "string"; }
void operator() (std::exception) const { std::cout << "exception"; }
};
entspricht ich eine Implementierung erstellt, aber es lässt sich nicht kompilieren mit Klirren C++ 11.
Ich bin mir nicht sicher, ob es einen Compiler Bug oder einen Fehler in meinem Code gibt. Ich suche nach Workarounds oder vielleicht eine bessere Lösung, die mit gcc und clang im C++ 11-Modus kompilieren würde.
#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:
habe ich versucht, eine andere Lösung und erhalten expliziter Signaturen in make_callable Template-Parameter befreien. So, jetzt die Compose-Funktion ist in einer solchen einfachen Weg genannt:
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"; }
}
Und hier ist Coliru Demo. Aber ich bin mir immer noch nicht sicher, ob es sehr effektiv ist oder eine viel bessere Lösung möglich ist.
Für das, was es wert ist, Ihr Code erscheint mit [gcc] (http zu arbeiten: // rextester.com/REA61905) und [VC++] (http://rextester.com/QYSBXB97285), aber nicht klingeln. –
Aus Neugier: Warum sollten Sie jemals die nicht-strikte Version verwenden? Ich würde immer bevorzugen, dass der Compiler mir sagt, dass die Signatur falsch ist. – Rumburak