2016-03-24 2 views
9

Lorsque je passe un flottant complexe (complex.h) d'un appelant C++ vers une bibliothèque ac, la valeur ne passe pas correctement lors de l'exécution un PC de puissance de 32 bits. J'utilisais deux bibliothèques logicielles open source différentes lorsque j'ai détecté ce problème. Je l'ai isolé à la limite de quand C++ passe un type de valeur complexe à une fonction de type C pure. J'ai écrit un code simple pour le démontrer.Les nombres complexes passés de C++ à C ne semblent pas fonctionner sur powerpc

#ifndef MMYLIB_3A8726C1_H 
#define MMYLIB_3A8726C1_H 

typedef struct aComplexStructure { 
    float r; 
    float i; 
} myComplex_t; 

#ifdef __cplusplus 
#include <complex> 
extern "C" { 
    void procWithComplex(float a, std::complex<float> *pb, std::complex<float> c, float d); 
    void procWithStruct(float a, myComplex_t *pb, myComplex_t c, float d); 
} 

#else /* __cplusplus */ 

#include <complex.h> 
void procWithComplex(float a, float complex *pb, float complex c, float d); 
void procWithStruct(float a, myComplex_t *pb, myComplex_t c, float d); 

#endif 

#endif /* MYLIB_3A8726C1_H */ 

Le fichier source C est comme suit

#include <stdio.h> 
#include "myLib.h" 

void procWithComplex(float a, complex float * pb, complex float c, float d) 
{ 
    printf("a=%f\n", a); 
    printf("b=%f + %fi\n", creal(*pb), cimag(*pb)); 
    printf("c=%f + %fi\n", creal(c), cimag(c)); 
    printf("d=%f\n", d); 
} 


void procWithStruct(float a, myComplex_t* pb, myComplex_t c, float d) 
{ 
    printf("a=%f\n", a); 
    printf("b=%f + %fi\n", pb->r, pb->i); 
    printf("c=%f + %fi\n", c.r, c.i); 
    printf("d=%f\n", d); 
} 

Le programme appelant C++ est comme suit

#include <iostream> 
#include "myLib.h" 

int main() 
{ 
    float a = 1.2; 
    std::complex<float> b = 3.4 + 3.4I; 
    std::complex<float> c = 5.6 + 5.6I; 
    float d = 9.876; 

    myComplex_t b_s, c_s; 

    b_s.r = b.real(); 
    b_s.i = b.imag(); 

    c_s.r = c.real(); 
    c_s.i = c.imag(); 

    std::cout << "a=" << a << std::endl; 
    std::cout << "b=" << b << std::endl; 
    std::cout << "c=" << c << std::endl; 
    std::cout << "d=" << d << std::endl << std::endl; 

    // c is a 64 bit structure being passed by value. 
    // on my 32 bit embedded powerpc platform, it is being 
    // passed by reference, but the underlying C library is 
    // reading it by value. 
    procWithComplex(a, &b, c, d); 
    std::cout << std::endl; 

    // This is only here to demonstrate that a 64 bit value field 
    // does pass through the C++ to C boundry 
    procWithStruct(a, &b_s, c_s, d); 
    return 0; 
} 

Normalement, j'attendre que la sortie soit

a=1.2 
b=(3.4,3.4) 
c=(5.6,5.6) 
d=9.876 

a=1.200000 
b=3.400000 + 3.400000i 
c=5.600000 + 5.600000i 
d=9.876000 

a=1.200000 
b=3.400000 + 3.400000i 
c=5.600000 + 5.600000i 
d=9.876000 

Mais quand je cours la source sur un n La machine de puissance embarquée de puissance Je reçois la production qui montre que le type de valeur pour le complexe n'est pas passé correctement.

a=1.2 
b=(3.4,3.4) 
c=(5.6,5.6) 
d=9.876 

a=1.200000 
b=3.400000 + 3.400000i 
c=-0.000000 + 9.876000i 
d=0.000000 

a=1.200000 
b=3.400000 + 3.400000i 
c=5.600000 + 5.600000i 
d=9.876000 

J'ai vérifié les sizeof les paramaters de gdb et à la fois l'appel et le cadre de fonction, les tailles sont 4 octets, 4 octets, 8 octets et 4 octets, pour le flotteur, le pointeur de flotteur complexe, flotteur complexe, et flotter. Je réalise que je peux simplement changer le paramètre de valeur complexe en tant que pointeur, ou ma propre structure lorsque je croise la limite de C++ en c, mais je veux savoir pourquoi je ne peux pas passer un type de valeur complexe de C++ à c un pc de puissance.

J'ai créé un autre exemple seulement cette fois que j'ai jeté une partie de l'ensemble ainsi que les valeurs de registre.

int x = 22; 
std::complex<float> y = 55 + 88I; 
int z = 77; 
void simpleProc(int x, complex float y, int z) 

Juste avant appel où les paramètres sont passés dans.

x = 22 
y = {_M_value = 55 + 88 * I} 
Looking at raw data *(int*)&y = 1113325568 
z = 77 

Ce devrait être le code assembleur où il enregistre le l'adresse de retour et enregistre les paramters à passer dans la routine.

x0x10000b78 <main()+824> lwz  r9,40(r31) 
x0x10000b7c <main()+828> stw  r9,72(r31) 
x0x10000b80 <main()+832> lwz  r9,44(r31) 
x0x10000b84 <main()+836> stw  r9,76(r31) 
x0x10000b88 <main()+840> addi r9,r31,72  
x0x10000b8c <main()+844> lwz  r3,16(r31) 
x0x10000b90 <main()+848> mr  r4,r9  
x0x10000b94 <main()+852> lwz  r5,20(r31) 
x0x10000b98 <main()+856> bl  0x10000f88 <simpleProc> 

En regardant l'assemblée juste après la branche :)

x0x10000f88 <simpleProc>   stwu r1,-48(r1) 
x0x10000f8c <simpleProc+4>  mflr r0  
x0x10000f90 <simpleProc+8>  stw  r0,52(r1) 
x0x10000f94 <simpleProc+12>  stw  r29,36(r1) 
x0x10000f98 <simpleProc+16>  stw  r30,40(r1) 
x0x10000f9c <simpleProc+20>  stw  r31,44(r1) 
x0x10000fa0 <simpleProc+24>  mr  r31,r1  
x0x10000fa4 <simpleProc+28>  stw  r3,8(r31) 
x0x10000fa8 <simpleProc+32>  stw  r5,12(r31) 
x0x10000fac <simpleProc+36>  stw  r6,16(r31) 
x0x10000fb0 <simpleProc+40>  stw  r7,20(r31) 
x0x10000fb4 <simpleProc+44>  lis  r9,4096 

Ce sont la valeur une fois que nous sommes tout à fait dans la routine (après les valeurs des variables sont affectées.

x = 22 
y = 1.07899982e-43 + 0 * I 
z = 265134296 

$r3 = 22 
$r4 = 0x9ffff938 
*(int*)$r4 = 1113325568 
$r5 = 77 

*(int*)(&y) = 77 

Mon la vue des profanes est qu'il semble que le C++ passe le type de valeur complexe comme référence ou type de pointeur? mais C le traite comme un type de valeur? Donc, est-ce un problème avec gcc sur le pc de puissance? J'utilise gcc4.7.1. Je suis un m en train de construire gcc4.9.3 en tant que compilateur croisé sur une autre machine. Je mettrai à jour ce post de toute façon une fois que je recevrai la sortie d'un compilateur plus récent. Si vous rencontrez des problèmes pour faire fonctionner le compilateur croisé, mais en regardant l'image mémoire du problème d'origine, cela montre que sur la plate-forme du PowerPC, la valeur complexe n'est pas transmise par valeur. J'ai mis l'exemple de la structure ici pour montrer qu'une valeur de 64 bits peut être transmise par valeur, sur une machine 32 bits.

+3

Ce n'est pas le problème, mais les noms qui commencent par un trait de soulignement suivi d'une lettre majuscule ('_MYLIB_H_') et les noms qui contiennent deux traits de soulignement consécutifs sont réservés à l'implémentation. Ne les utilisez pas. –

+0

@PeteBecker: Eh bien, il peut être, au moins en C, il pourrait invoquer UB en changeant le comportement de stdlib. – Olaf

+0

Vous pouvez vérifier les ABI (improbables) et vérifier le code de l'assembleur pour obtenir un indice. En général, vous devez utiliser les types standard. – Olaf

Répondre

4

Votre code provoque un comportement indéfini. Dans l'unité C++ la fonction est déclarée comme:

extern "C" void procWithComplex(float a, std::complex<float> *pb, std::complex<float> c, float d); 

mais le corps de la fonction est:

void procWithComplex(float a, complex float * pb, complex float c, float d) 

qui ne correspond pas.

Pour aider le compilateur à diagnostiquer cette erreur, évitez d'utiliser le préprocesseur pour basculer dans différents prototypes pour la même fonction.

Pour éviter cette erreur, vous devez avoir le prototype de fonction uniquement utiliser les types qui sont valides dans C et C++. Tels que vous l'avez fait dans l'exemple myComplex_t.

0

Nous avons fini par utiliser un compilateur croisé par rapport au compilateur natif sur la carte de développement pour créer les binaires. Les nombres apparemment complexes ne sont pas gérés correctement sur les limites C à C++ du compilateur natif que nous avons utilisé.

Tous les changements suggérés ont été essayés et ont échoué, mais ils étaient tous de bonnes suggestions. Cela nous a aidé à confirmer que c'était peut-être un problème de compilateur qui nous a permis d'essayer d'utiliser un compilateur croisé. Merci a tous!

+1

Ce n'est pas la bonne solution. Vous invoquez UB mais vous avez de la chance, et votre code pourrait exploser à tout moment. –