Invio dinamico con dati acquisiti in C?

4

Sto cercando di riconciliare la mia mente orientata agli oggetti / funzionale con la programmazione in una lingua con C. Diciamo che voglio ottenere una spedizione dinamica in C, diciamo che voglio avere una serie di attività da eseguire. Potrei creare un puntatore a una raccolta di funzioni che voglio eseguire, abbastanza semplice. Tuttavia, diciamo che l'attività da eseguire dipende dalle variabili che esistono quando la funzione viene creata e non verrà passata nella funzione da qualsiasi cosa venga eseguita. In Java o C ++, userei un oggetto (probabilmente un lambda) che cattura quelle variabili, in modo che la funzione possa accedervi quando viene eseguito. Ma ho sentito che i puntatori di funzione non possono catturare le variabili nello standard C. Quindi, come potrei implementare questo modello di progettazione? Grazie.

    
posta Phoenix 22.04.2017 - 20:07
fonte

2 risposte

5

In CS StackExchange, ho discusso di come è possibile codificare le funzioni di ordine superiore in termini di puntatori di funzione . Fondamentalmente, una chiusura è solo una coppia di void * per un ambiente e un puntatore di funzione. Qualcosa come:

struct IntToIntClosure {
    void *environment;
    int (*func)(void *, int);
}

Ad esempio, nell'API Asynchronous I / O di Linux sigevent la struttura fa unire queste cose insieme ("l'ambiente" è il campo sigev_value ).

Tuttavia, è molto comune passare un foo * e una lunghezza separatamente quando foo * è visto come una matrice, è normale non comprimerli in una struttura dati, ma per passarli come separati parametri. Molte API C che accettano i callback accettano un ulteriore parametro "context" che di solito è un void * che verrà passato al puntatore della funzione ogni volta che viene chiamato. Ad esempio, on_exit di glibc (in contrasto con atexit ).

Puoi facilmente codificare le funzioni e gli oggetti di ordine superiore con la spedizione dinamica usando queste tecniche. Tuttavia, se si dispone di un'API che accetta solo i puntatori di funzioni per la registrazione, non è possibile eseguire questo * . Non c'è modo di sapere quale ambiente deve essere associato a quale invocazione di un puntatore di funzione dopo il fatto. In questo caso, sarai costretto a utilizzare variabili globali o statiche per trasferire qualsiasi informazione sull'ambiente con le limitazioni relative a essenzialmente un solo ambiente consentito. Se hai il controllo di questa API, I consiglia strongmente di prendere e memorizzare un parametro aggiuntivo per questo ambiente (esplicitamente o tramite struct ).

* Ci sono trucchi per aggirare questo problema se non ti interessa la portabilità. Se è possibile generare dinamicamente i puntatori di funzione (ad esempio scrivendo il codice macchina in fase di esecuzione) è possibile creare un puntatore a funzione su uno stub che passerà il parametro aggiuntivo alla funzione desiderata. Ecco come GHC Haskell gestisce il passaggio delle funzioni Haskell come callback C.

    
risposta data 23.04.2017 - 01:52
fonte
4

Non c'è possibilità di cattura in C, e la funzione non è "creata" ma esiste poiché l'immagine eseguibile è caricata in memoria.

Ci sono fondamentalmente tre modi per fare ciò che vuoi:

  • usa le variabili globali per passare valori e puntatori alla funzione puntata. Inconvenienti: incapsulamento cattivo.
  • usa le variabili statiche in un'unità di compilazione separata, con una funzione di inizializzazione e la funzione target da chiamare. Questo è meglio dal punto di vista dell'incapsulamento. Tuttavia, sei ancora limitato a un singolo set di variabili "catturate".
  • usa un ricordo di contesto: è una variante dell'approccio precedente, ma invece di variabili statiche, usi i membri di una struttura allocata dinamicamente. Il puntatore a questo contesto verrebbe restituito dalla funzione di inizializzazione e verrebbe archiviato insieme al puntatore della funzione. Questa è una soluzione rientranti, garantendo un incapsulamento adeguato. Tuttavia, ciò richiede che il puntatore venga passato alla funzione chiamata. Naturalmente, diverse funzioni potrebbero avere diverse strutture di contesto (soggette a casting)
risposta data 22.04.2017 - 22:58
fonte

Leggi altre domande sui tag