2014-12-17 6 views
6

ich tun will:Vertretung für ein std :: Funktion auf eine C-Funktion, die mit einem C-System, das einen Haken dieser Form bietet eine Reihe von Argumenten

int (*EXTENSIONFUNCTION)(NATIVEVALUE args[]); 

Es ist möglich, eine EXTENSIONFUNCTION zu registrieren und die Anzahl der Argumente, die es braucht.

Meine Idee war, dass ich eine Klasse Extension machen würde, um eine Erweiterung abzuschließen. Es könnte in der Lage sein, aus einer std :: function (oder jeder Callable, idealerweise, aber lassen Sie uns einfach sagen, es enthält eine std :: -Funktion für jetzt) ​​. Und die Erweiterung nimmt Value-Parameter, die NATIVEVALUE (aber sind größer). Ich würde zum Beispiel automatisch die Anzahl der Parameter mit sizeof...(Ts) überwachen. Es könnte wie folgt aussehen:

Extension<lib::Integer, lib::String> foo = 
    [](lib::Integer i, lib::String s) -> int { 
     std::cout << i; 
     std::cout << s; 
     return 0; 
    } 

Problem ist, dass für die C-Bibliothek, um sie zu registrieren und rufen, sie will, dass die Array-basierte Schnittstelle. : -/

Ich machte mich daran, den Compiler zu versuchen, ein kleines Shim zu schreiben, aber ich sehe keinen Weg, es zu tun. Ich kann eine variadic operator() auf Erweiterung haben, und eine Laufzeitschleife über den NATIVEVALUE, um ein Array von Value [] zu erhalten. Aber was mache ich damit? Ich kann die std :: -Funktion damit nicht aufrufen.

So scheint es, ich muss eine EXTENSIONFUNCTION-Instanz machen, die meine std :: -Funktion aufruft, als Mitglied jeder Extension-Instanz.

Aber im Grunde finde ich mich gegen eine Wand, wo ich eine variadic gestempelte Klasse für die Erweiterung habe ... und dann eine Art von "kann nicht von hier kommen" in Bezug auf diese NATIVEVALUE args[] und in der Lage zu sein Rufen Sie die std :: -Funktion mit ihnen auf. Wenn std :: function bereit wäre, mit einem std :: -Array von Argumenten aufgerufen zu werden, würde das das Problem lösen, aber so funktioniert es natürlich nicht.

Ist es möglich, eine Scheibe dieses Typs zu bauen? Das „hässlich“, was ich tun kann, ist nur Proxy auf einem anderen Array, wie:

Extension<2> foo = 
    [](lib::Value args[]) -> int { 
     lib::Integer i (args[0]); 
     lib::String s (args[1]); 
     std::cout << i; 
     std::cout << s; 
     return 0; 
    } 

Aber das ist nicht so ergonomisch. Es scheint unmöglich zu sein, ohne die Aufrufkonvention zu kennen und eine Art Inline-Assembly zu machen, um die Parameter zu verarbeiten und die Funktion aufzurufen (und sogar das würde nur für Funktionen funktionieren, nicht Callables im Allgemeinen). Aber die Leute hier haben, bevor das Unmögliche möglich erwiesen, in der Regel im Wege der „das ist nicht das, was Sie wollen, was Sie wirklich wollen, ist ...“


UPDATE: Ich fand gerade diese, die scheint viel versprechend ... ich versuche immer noch ihre Relevanz zu verdauen:

"unpacking" a tuple to call a matching function pointer

(Anmerkung: Es gibt ein paar Querschnittsthemen in dem, was ich tun wollen ist ein weiterer Punkt Folgerung aus lambdas eingeben.. Antwort hier scheint die beste zu sein wettet, dass ... es scheint zu funktionieren, aber ich weiß nicht, ob es „koscher“ ist: Initialize class containing a std::function with a lambda)

+0

Da 'EXTENSIONFUNCTION' anscheinend keine' void * userData' benötigt, um Ihre Klasse 'Extension' aufrufen zu können, sehe ich nicht, wie Sie Ihr Problem lösen können. – Jarod42

+0

Was ist 'NATIVEVALUE'? Vielleicht können Sie im ersten Element des Arrays einen Zeiger auf 'Extension' übergeben. –

+0

Ich hatte eine etwas schwierige Zeit zu lesen, was Sie hier reingelegt haben, aber ich * denke * vielleicht ist es [so etwas] (http://ideone.com/fJlkK6). Wenn nicht, hoffe ich, dass es dir wenigstens ein paar Ideen gibt. Viel Glück. – WhozCraig

Antwort

2

Wenn ich das Problem auf seine einfachste Form zu reduzieren verwaltet werden, müssen Sie einen Weg, um ein std::function nennen nimmt sein Argument von einem C-artigen Array fester Größe, ohne eine Laufzeitschleife erstellen zu müssen.Dann können diese Funktionen Ihr Problem lösen:

template<std::size_t N, typename T, typename F, std::size_t... Indices> 
auto apply_from_array_impl(F&& func, T (&arr)[N], std::index_sequence<Indices...>) 
    -> decltype(std::forward<F>(func)(arr[Indices]...)) 
{ 
    return std::forward<F>(func)(arr[Indices]...); 
} 

template<std::size_t N, typename T, typename F, 
     typename Indices = std::make_index_sequence<N>> 
auto apply_from_array(F&& func, T (&arr)[N]) 
    -> decltype(apply_from_array_impl(std::forward<F>(func), arr, Indices())) 
{ 
    return apply_from_array_impl(std::forward<F>(func), arr, Indices()); 
} 

Hier ist ein Beispiel wird demonstriert, wie es verwendet werden kann:

auto foo = [](int a, int b, int c) 
    -> int 
{ 
    return a + b + c; 
}; 

int main() 
{ 
    Value arr[] = { 1, 2, 3 }; 

    std::cout << apply_from_array(foo, arr); // prints 6 
} 

Natürlich mit der Unterschrift int (*)(T args[]) ist args nur T* und Sie don kenne seine Größe zur Kompilierzeit nicht. Wenn Sie jedoch die Kompilierung Größe von woanders (aus dem std::function zum Beispiel) kennen, können Sie immer noch apply_from_array zwicken, um manuell die Kompilierung-Größeninformationen zu geben:

template<std::size_t N, typename T, typename F, std::size_t... Indices> 
auto apply_from_array_impl(F&& func, T* arr, std::index_sequence<Indices...>) 
    -> decltype(std::forward<F>(func)(arr[Indices]...)) 
{ 
    return std::forward<F>(func)(arr[Indices]...); 
} 

template<std::size_t N, typename T, typename F, 
     typename Indices = std::make_index_sequence<N>> 
auto apply_from_array(F&& func, T* arr) 
    -> decltype(apply_from_array_impl<N>(std::forward<F>(func), arr, Indices())) 
{ 
    return apply_from_array_impl<N>(std::forward<F>(func), arr, Indices()); 
} 

Und dann die Funktion wie folgt verwenden:

int c_function(NATIVEVALUE args[]) 
{ 
    return apply_from_array<arity>(f, args); 
} 

In dem obigen Beispiel der Ansicht, dass f ein std::function ist und dass arity ist die arity von f, die Sie erhalten verwaltet, eine oder andere Weise, bei der Kompilierung.

HINWEIS: verwenden ich die C++ 14 std::index_sequence und std::make_index_sequence aber wenn Sie Ihren Code müssen mit C++ 11 arbeiten, können Sie immer noch handwerklich Äquivalente verwenden, wie indices und make_indices in der alten Frage von mir, dass du hast verlinkt.


Aftermath: die Frage ist, über real code, ist es natürlich ein wenig komplizierter als oben. Der Erweiterungsmechanismus ist so konzipiert, dass jedesmal, wenn eine Erweiterungsfunktion aufgerufen wird, C++ - Proxys über der C-API (lib::Integer, lib::String usw.) im laufenden Betrieb erzeugt und dann an die benutzerdefinierte Funktion übergeben werden. Dies erforderte eine neue Methode, applyFunc in Extension:

template<typename Func, std::size_t... Indices> 
static auto applyFuncImpl(Func && func, 
          Engine & engine, 
          REBVAL * ds, 
          utility::indices<Indices...>) 
    -> decltype(auto) 
{ 
    return std::forward<Func>(func)(
     std::decay_t<typename utility::type_at<Indices, Ts...>::type>{ 
      engine, 
      *D_ARG(Indices + 1) 
     }... 
    ); 
} 

template < 
    typename Func, 
    typename Indices = utility::make_indices<sizeof...(Ts)> 
> 
static auto applyFunc(Func && func, Engine & engine, REBVAL * ds) 
    -> decltype(auto) 
{ 
    return applyFuncImpl(
     std::forward<Func>(func), 
     engine, 
     ds, 
     Indices {} 
    ); 
} 

applyFunc die Funktion nimmt eine Anruf mit Instanzen des entsprechenden Typs zu nennen (Integer, String, etc ...) on the fly von dem zugrunde liegenden C-API erstellt im laufenden Betrieb mit einem Engine& und einem REBVAL*.

+0

Nochmals vielen Dank für Ihre Hilfe ... über den Ruf der Pflicht hinaus ...! – HostileFork