Ja, das ist möglich:
// we need a compile-time helper to generate indices
template< std::size_t... Ns >
struct indices
{
typedef indices< Ns..., sizeof...(Ns) > next;
};
template< std::size_t N >
struct make_indices
{
typedef typename make_indices< N - 1 >::type::next type;
};
template<>
struct make_indices<0>
{
typedef indices<> type;
};
Mit diesen Helfern, benötigen Sie einen Spediteur für Ihre Funktion wie folgt aus:
template<typename R, typename... Args, std::size_t... Ns>
R myFunctionImpl(void *Data, void *function, indices<Ns...>) {
auto f = (R (*)(Args...))function;
return f(read<Args>(Data, Ns + 1)...);// +1 because indices is zero-based
}
template<typename R, typename... Args>
R myFunction(void *Data, void *function) {
return myFunctionImpl< R, Args... >(Data, function, typename make_indices<sizeof...(Args)>::type());
}
EDIT: Wie funktioniert es? Zuerst bestimmen wir die Größe des argument packArgs
durch . make_indices<N>::type
expandiert dann in indices<0,1,2,...,N-1>
. Es wird als zusätzlicher Parameter für die Implementierungsfunktion angegeben (von der Weiterleitung, die nur eine Dummy-Instanz erstellt), daher beginnt der Argumentabzug auf der Seite der Implementierungsfunktion und setzt die generierten Indizes in das Argumentpack Ns
.
Die Implementierungsfunktion hat jetzt zwei Argumentpakete mit der gleichen Größe, nämlich Args
und Ns
. Wenn sie durch die Ellipse ...
erweitert wird, erweitert die Ellipse den ganzen Ausdruck, auf den es angewendet wird, und es erweitert alle Parameterpakete parallel! Im obigen Beispiel ist dieser Ausdruck read<Args>(Data, Ns+1)
, was sich schön in den OPs-Pseudocode erweitert.
Ich bin sicher, Sie können den Objektzeiger auf Funktionszeiger-Cast vermeiden. – sellibitze
@sellibitze: Ist das ein Problem? Weil ich nur void * pointer Benutzerdaten haben kann, die von einer C-Funktion übergeben werden. –
Die Daten können ein 'void *' sein. Es ist die Funktion, eine "Leere" zu sein, die ein Problem darstellt. –