Комплексные числа, передаваемые по значению из C ++ в C, похоже, не работают на powerpc

Когда я передаю сложное число с плавающей точкой (complex.h) из вызывающей стороны c ++ в библиотеку c, значение не передается правильно при работе на 32-битном мощном ПК. Когда я обнаружил эту проблему, я использовал две разные библиотеки программного обеспечения с открытым исходным кодом. Я выделил его до предела того, когда C ++ передает сложный тип значения в чистую функцию типа C. Я написал простой код, чтобы продемонстрировать это.

#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 */

Исходный файл C выглядит следующим образом

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

Вызывающая программа C ++ выглядит следующим образом

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

Обычно я ожидаю, что результат будет

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

Но когда я запускаю источник на встроенном мощном компьютере, я получаю вывод, который показывает, что тип значения для complex не передается должным образом.

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

Я проверил размер параметров из gdb, а также из вызывающего и функционального фреймов размеры 4 байта, 4 байта, 8 байтов и 4 байта для float, сложного указателя float, сложного float и float.

Я понимаю, что могу просто изменить параметр комплексного значения в качестве указателя или мою собственную структуру при пересечении границы c ++ в c, но я хочу знать, почему я не могу передать тип сложного значения из c ++ в c на мощном ПК.

Я создал другой пример только в этот раз, когда я сбросил некоторые сборки, а также значения регистров.

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

Прямо перед вызовом, где передаются параметры.

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

Это должен быть код сборки, где он сохраняет адрес возврата и сохраняет параметры для передачи в подпрограмму.

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>

Смотря на сборку сразу после ветки 🙂

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

Это значение, когда мы полностью находимся в процедуре (после присвоения значений переменных.

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

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

*(int*)(&y) = 77

Мой взгляд на неспециалистов: похоже, C ++ передает сложный тип значения как ссылочный или указательный тип? но C рассматривает это как тип значения? Так это проблема с gcc на мощном ПК? Я использую gcc4.7.1. Я нахожусь в процессе сборки gcc4.9.3 в качестве кросс-компилятора на другой машине. Я обновлю этот пост в любом случае, как только получу вывод от нового компилятора.

Имея проблемы с работой кросс-компилятора, но глядя на дамп памяти исходной проблемы, он показывает, что на платформе power pc комплексное значение не передается по значению. Я привел здесь пример структуры, чтобы показать, что 64-битное значение может передаваться по значению на 32-битной машине.

9

Решение

Ваш код вызывает неопределенное поведение. В модуле C ++ функция объявлена ​​как:

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

но тело функции:

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

который не соответствует.

Чтобы помочь компилятору диагностировать эту ошибку, вам следует избегать использования препроцессора для переключения разных прототипов для одной и той же функции.

Чтобы избежать этой ошибки, необходимо, чтобы в прототипе функции использовались только те типы, которые допустимы как в C, так и в C ++. Такие, как вы сделали в myComplex_t пример.

4

Другие решения

В итоге мы создали кросс-компилятор против нативного компилятора на плате разработчика для создания двоичных файлов. Очевидно, сложные числа не обрабатываются должным образом через границу C — C ++ для используемого нами собственного компилятора.

Все предложенные изменения были опробованы и потерпели неудачу, но все они оставались хорошими предложениями. Это помогло подтвердить наши мысли о том, что это может быть проблема с компилятором, которая позволила нам попробовать использовать кросс-компилятор. Спасибо всем!

0

По вопросам рекламы [email protected]