2011-05-27 16 views
7

consideran un simple clase de ejemplo:Envolver objeto de C++ en extern "C"

class BankAccount { 
public: 
    BankAccount() { balance =0.0; }; 
    ~BankAccount() {}; 
    void deposit(double amount) { 
     balance += amount; 
    } 
    private: 
     double balance; 
}; 

Ahora digo que quiero terminar con esto en extern "C", por lo que se puede llamar de muchas diferentes lenguajes de programación como C# y Java. Probé la continuación de lo cual parecía funcionar:

// cbankAccount.h: 
extern "C" unsigned long createBackAccount(); 
extern "C" void deposit(unsigned long bankAccount, double amount); 
// cbankAccount.cpp 
unsigned long createBackAccount() { 
    BankAccount *b = new BankAccount(); 
    return (unsigned long) b; 
} 
void deposit(unsigned long bankAccount, double amount) { 
    BankAccount *b = (BankAccount*) bankAccount; 
    b->deposit(amount); 
} 

Es este portátil? ¿El tipo sin signo "sin signo largo" es lo suficientemente grande para un puntero de objeto? ¿Algún otro problema con este enfoque?

¡Gracias de antemano por cualquier respuesta!

+0

¿Cómo van a acceder esos otros idiomas a 'BankAccount' si no tienen la definición de clase? –

+2

@Etienne, a través del puntero (emitido como un largo) y las funciones C. Una C-API para una biblioteca C++, supongo. – Skurmedel

+0

@Skudermel Hmm, sí, eso tiene sentido. –

Respuesta

9

Que básicamente se ve bien con una condición. Intentar utilizar un tipo de entero para contener un puntero no es una gran idea; es mucho mejor usar void* ya que, por definición, es el ancho de un puntero.


En realidad, creo que la respuesta de @ DeadMG es un enfoque más limpio que este.

3

Esto no es portátil, porque unsigned long puede no ser suficiente para un puntero. Una plataforma no tan rara donde sucede esto es win64.

Es mejor usar ptrdiff_t o void*.

+1

El tipo correcto de stdlib es 'intptr_t' no' ptrdiff_t'. – Xeo

10

Sí. Es malo. Muy mal. unsigned long - simplemente no. Devuelve un BankAccount* correctamente tipado; los otros idiomas lo verán en el otro extremo como un puntero genérico (como System.IntPtr) y no es necesario devolver un puntero sin tipo cuando la interfaz binaria no escribe punteros de todos modos.

extern "C" BankAccount* CreateBankAccount() { 
    return new BankAccount; 
} 
extern "C" void deposit(BankAccount* account, double amount) { 
    account->deposit(amount); 
} 
+0

+1 Si lo hiciera, tendría que escribir un archivo de encabezado diferente para cualquier cliente C. Como cualquier cliente C++ no va a usar la versión plana de la interfaz, 'void *' tiene más sentido. ¡Pero tal vez no haya ningún cliente C, en cuyo caso su enfoque es mejor! –

+0

@David: '#ifndef __cplusplus typedef struct Cuenta bancaria BankAccount; # endif' – Puppy

+0

Actualmente, estoy trabajando en la interfaz API de mi aplicación y, aunque está escrita en Delphi, utilizo exactamente la misma estrategia que usted describe en su respuesta en el límite de mi biblioteca. –

3

Probablemente sería dependiente de la plataforma ya sea larga es lo suficientemente grande (probablemente no en x86-64) una alternativa es usar algo como swig

2

que haría uso de void * en lugar de largo sin signo.

5

Type-strong es incluso mejor que void *.

typedef struct BankAccountProxy * BankAccountPtr; 

BankAccountPtr createBackAccount() { 
    BankAccount *b = new BankAccount(); 
    return (BankAccountPtr) b; 
} 
Cuestiones relacionadas