2014-12-17 14 views
6

ho a che fare con un sistema di C che offre un gancio di questa forma:Proxying uno std :: funzione a una funzione C che vuole una matrice di argomenti

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

E 'possibile registrare un EXTENSIONFUNCTION e la numero di argomenti necessari.

La mia idea era che avrei creato una classe Extension per concludere un'estensione. Sarebbe in grado di essere costruito da una funzione std :: (o qualsiasi Callable, idealmente, ma diciamo solo che contiene una funzione std :: per ora). E l'estensione accetta i parametri Value, che contengono NATIVEVALUE (ma sono maggiori). Mi occuperei automaticamente del conteggio dei parametri con sizeof...(Ts), ad esempio. Si potrebbe assomigliare a questo:

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

Il problema è che, al fine per la libreria C per registrare e chiamarlo, vuole che l'interfaccia basata su array. : -/

Ho cercato di convincere il compilatore a scrivere un piccolo shim, ma non vedo il modo di farlo. Posso avere un variadic operator() su Extension, ed eseguire un loop di runtime sul NATIVEVALUE per ottenere un array di Value []. Ma cosa faccio con quello? Non riesco a chiamare la funzione std :: con esso.

Quindi sembra che sia necessario creare un'istanza EXTENSIONFUNCTION che chiama la mia std :: function, come membro di ogni istanza Extension.

Ma fondamentalmente mi trovo di fronte a un muro dove ho una classe di moduli variadici per l'estensione ... e quindi una sorta di "non posso arrivarci da qui" in termini di prendere questo NATIVEVALUE args[] e poterlo fare chiama la funzione std :: con loro. Se std :: function fosse disposto a essere invocato con uno std :: array di argomenti, questo lo risolverebbe, ma ovviamente non è così che funziona.

È possibile creare uno spessore di questo tipo? La cosa "brutto" che posso fare è solo proxy per un altro array, come:

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

Ma non è come ergonomico. Sembra impossibile, senza conoscere la convenzione di chiamata e fare una sorta di assembly inline per elaborare i parametri e CHIAMARE la funzione (e anche quella funzionerebbe solo per le funzioni, non Callables in generale). Ma la gente qui hanno dimostrato possibile l'impossibile prima, di solito per mezzo di "che non è quello che vuoi, quello che realmente vuole è ..."


UPDATE: Ho appena trovato questo, che sembra promettente ... sto ancora cercando di digerire la sua rilevanza:

"unpacking" a tuple to call a matching function pointer

(Nota: ci sono un paio di questioni trasversali in quello che mi propongo di fare un altro punto è di tipo inferenza da lambda.. La risposta qui sembra essere la migliore scommettere su che ... sembra funzionare, ma non so se si tratta di "kosher": Initialize class containing a std::function with a lambda)

+0

Poiché 'EXTENSIONFUNCTION' non sembra richiedere un' void * userData' per poter chiamare la propria 'Extension', non vedo come risolvere il problema. – Jarod42

+0

Che cos'è 'NATIVEVALUE'? Forse puoi passare un puntatore a 'Extension' nel primo elemento dell'array. –

+0

Ho avuto un tempo un po 'difficile leggere quello che hai inserito qui, ma io * penso * forse è [qualcosa di simile] (http://ideone.com/fJlkK6). Altrimenti, spero che almeno ti dia qualche idea. Buona fortuna. – WhozCraig

risposta

2

Se sono riuscito a ridurre il problema alla sua forma più semplice, è necessario un modo per chiamare un std::function prendendo la sua argomentazione da una matrice in stile C di dimensioni fisse senza dover creare un ciclo di runtime.Quindi, queste funzioni possono risolvere il problema:

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

Ecco un esempio che dimostra come esso può essere utilizzato:

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 
} 

Naturalmente, con la firma int (*)(T args[]), args è solo un T* e don Conosco le sue dimensioni al momento della compilazione. Tuttavia, se si conosce la dimensione tempo di compilazione da qualche altra parte (dal std::function per esempio), è ancora possibile modificare apply_from_array per dare manualmente le informazioni sulle dimensioni in fase di compilazione:

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

e quindi utilizzare la funzione in questo modo:

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

nell'esempio di cui sopra, si consideri che f è un std::function e che arity è l'arietà di f che è riuscito a ottenere, in un modo o nell'altro, al momento della compilazione.

NOTA: Ho usato il C++ 14 std::index_sequence e std::make_index_sequence, ma se è necessario il codice per lavorare con C++ 11, è comunque possibile utilizzare gli equivalenti artigianali, come indices e make_indices nella vecchia questione che in me hai collegato.


Aftermath: la domanda che su real code, era ovviamente un po 'più complicata di quanto sopra. Il meccanismo di estensione è progettato in modo tale che ogni volta che viene chiamata una funzione di estensione, i proxy C++ sopra l'API C (lib::Integer, lib::String, ecc.) Vengono creati al volo e poi passati alla funzione definita dall'utente. Ciò ha richiesto un nuovo metodo, 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 prende la funzione di chiamare un chiamate con istanze dei tipi appropriati (Integer, String, ecc ...) al volo dal sottostante C API creato al volo con uno Engine& e uno REBVAL*.

+0

Grazie ancora per tutto il tuo aiuto ... al di sopra e al di là del richiamo del dovere ...! – HostileFork

Problemi correlati