2012-01-25 12 views
6

Questa è una versione completamente riscritta di an earlier question; Penso che la prima versione abbia omesso dettagli importanti; questo fornisce tutto il contesto.C/C++ API puzzle

Ho un'intestazione di alcune API C++. L'API dichiara alcune classi come questa:

class Foo 
{ 
public: 
    inline void Bar(void); 
    /* more inlines */ 
private: 
    inline Foo(); 
    /* maybe more inline constructors */ 
} 

I.e. nessun membro, tutte le funzioni sono inline e pubbliche, tranne i costruttori. I costruttori sono privati, quindi, per quanto comprendo il C++, non posso davvero chiamarli. Per creare questi oggetti che dovrei usare auto_ptr s loro:

class FooAutoPtr : public std::auto_ptr<Foo> 
{ 
public: 
    inline FooAutoPtr(); 
    /* one for each Foo constructors */ 
} 

L'API ha anche una seconda serie di funzioni in questo modo:

void Foo_Bar(void *self); 
Foo* Foo_Constructor(); 

Chiamiamoli funzioni fondamentali, perché questi sono i simboli effettivamente esportati dall'app host. Non le classi C++.

Le funzioni principali hanno il collegamento C (ad esempio dichiarato come extern "C"), ma sono dichiarate come prendere e restituire tipi C++ (ad esempio possono prendere un riferimento: Foo &foo). Infine l'intestazione contiene l'implementazione delle funzioni inline delle classi C++. Tutte queste funzioni fanno lo stesso: chiamano le funzioni principali. Ad esempio, il costruttore FooAutoPtr è come questo:

inline FooAutoPtr::FooAutoPtr() 
{ 
    reset(Foo_Constructor()); 
} 

Da quello che ho capito, il codice riceve un oggetto che si suppone essere un puntatore a Foo dalla app host e cambia il auto_ptr aggeggio per puntare a questo oggetto . Ma allo sviluppatore sembra che fosse un vero puntatore a Foo. E chiamando Foo::Bar() va come questa:

inline Foo::Bar() 
{ 
    Foo_Bar(this); 
} 

Questo vale per tutte le classi e metodi C++. Intelligente, eh?

Ora, qualcuno potrebbe spiegare cosa significa tutto questo? :) Non è una vera API C++, vero? Per me sembra più un sottile wrapper C++ su una API C. In tal caso, posso ridichiarare le funzioni principali per perdere i bit C++? Capisco come scrivere un wrapper C attorno al C++ (in realtà, l'ho già scritto), ma, se possibile, preferirei perdere il wrapper e usare direttamente le funzioni. Ma come faccio a perdere la roba in C++?

Per esempio, ci potrebbe essere una funzione con riferimenti:

Bar& Foo_GetBar(void* self, const Baz& baz, int& i); 

In questo momento mi chiamare dal mio C++ involucro in questo modo:

typedef struct bar bar_t; /* and others */ 
/*...*/ 
bar_t* 
foo_get_bar(foo_t* foo, baz_t* baz, int* i) 
{ 
    return (bar_t*) &Foo_GetBar(foo, *(Baz*)baz, *i); 
} 

e funziona (non ho idea, Come). Ma avrei preferito che ridichiarato come questo:

/* type? */ Foo_GetBar(foo_t*, /* type? /*, /* type? */); 

UPDATE: ho trovato una cosa interessante che conferma Neil's answer. È un codice in Common Lisp che utilizza la stessa API. (Naturalmente, deve usare la parte C.) E, da quello che posso (a malapena) leggere nella fonte, l'autore ha semplicemente usato puntatori al posto di riferimenti.Ecco un frammento dal codice che converte le dichiarazioni C++ in Lisp:

;; use * instead of & - we're not interested in C++ details 
line (regex-replace-all "&" line "*") 

Quindi il gioco è fatto :) Grazie a tutti!

+8

Wow, una domanda che è _legitimately_ taggata sia 'c' che' C++ '. Un aspetto raro ... – ildjarn

+2

Se le funzioni di base restituiscono tipi C++ (riferimenti), allora si tratta di un'API C++ reale (_not_ un'API C), ma non di uno orientato agli oggetti. –

+0

@SethCarnegie Ecco fatto :) Si vede, l'intestazione ha anche un commento vicino alle funzioni principali dicendo che queste sono "per l'ambiente C-only". Bene, un altro commento obsoleto :) –

risposta

4

In teoria, i dettagli di come un compilatore tratta un riferimento in una dichiarazione di collegamento C è un dettaglio di implementazione non specificato. Tuttavia molti compilatori lo trattano come se fosse un puntatore. Pertanto, se l'intestazione della libreria C++ importa Bar& Foo_GetBar(void* self, const Baz& baz, int& i);, puoi provare a importarla nell'intestazione C come bar_t* Foo_GetBar(foo_t* self, const baz_t* baz, int* i); e potrebbe funzionare.

+0

Farò un tentativo. Sospettavo qualcosa del genere, ma non ero troppo coraggioso per iniziare senza una conferma :) Grazie! –

+0

BTW, ho aggiornato il post sul codice Lisp ho appena trovato che utilizza la stessa API e sì, semplicemente utilizza i puntatori :) Quindi hai ragione. –

1
#define & * // convert references into pointers. 

Stop. Destra. Là. Era uno scherzo, piuttosto che un tentativo di vedere quanti downvote potevano essere accumulati su una singola risposta.

Ma l'approccio di riscrivere il file di intestazione per sostituire i riferimenti con i puntatori dovrebbe sicuramente funzionare. Poiché si tratta di funzioni C, non c'è alcun problema di manomissione dei nomi e devo ancora vedere un compilatore che non implementa riferimenti come puntatori. (C'è qualche altro modo possibile?)

+0

'# define' funziona solo per identificatori, quindi non puoi fare questo trucco :) –

+0

@Seth: Infatti. Il che è altrettanto buono, davvero. – Roddy