1

I have some C DSP libraries which use complex [T] types for complex numbers. I want to call these from a C++ application which uses std::complex<T>.

After reading this SO answer and this one, and §26.4 of N4296, I tried an experiment:

extern "C" {
#include <complex.h>
// Using _Complex or __complex__ since C's "complex" type doesn't exist in C++
void cfunc(_Complex float x);
}
#include <complex>

void test()
{
        std::complex<float> z;
        cfunc(reinterpret_cast<float[2]>(z));
}

And tried to compile it with CXXFLAGS="-std=c++11". I got the following from GCC 6.3.1:

error: invalid cast from type ‘std::complex<float>’ to type ‘float [2]’
  cfunc(reinterpret_cast<float[2]>(z));

Is this a compiler bug or am I misunderstanding something? How would one use C functions which take complex [T] arguments, given C++ code using std::complex<T> types? Currently, I use a dirty hack to work around this issue but I'd prefer a clean way.

I tried compiling with -std=c++14 just in case this feature had missed C++11 somehow (despite posts quoting it from the C++11 standard) but I get the same result.


26.4 Complex numbers [complex.numbers]

  1. The header <complex> defines a class template, and numerous functions for representing and manipulating complex numbers.

  2. The effect of instantiating the template complex for any type other than float, double, or long double is unspecified. The specializations complex<float>, complex<double>, and complex<long double> are literal types (3.9).

  3. If the result of a function is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.

  4. If z is an lvalue expression of type cv std::complex<T> then:

    4.1. the expression reinterpret_cast<cv T(&)[2]>(z) shall be well-formed,

    4.2. reinterpret_cast<cv T(&)[2]>(z)[0] shall designate the real part of z, and

    4.3. reinterpret_cast<cv T(&)[2]>(z)[1] shall designate the imaginary part of z.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
Mark K Cowan
  • 1,755
  • 1
  • 20
  • 28
  • Read the notes on the `` header file: http://en.cppreference.com/w/cpp/header : _" (deprecated) simply includes the header "_ ... _" (since C++11)(deprecated in C++17) "_ – Richard Critten May 25 '17 at 12:07
  • @RichardCritten: Would you mind pasting a snippet of the relevant section so we don't all have to chase it down? – John Zwinck May 25 '17 at 12:09
  • @RichardCritten : Thanks. In my case, `complex.h` is included by the C headers rather than the C++ code. I added it in the experiment since it'll be included by my application (via the C library headers) – Mark K Cowan May 25 '17 at 12:10

1 Answers1

5

You problem is you are trying to cast to a value instead of a reference. The standard guarantees that

reinterpret_cast<cv T(&)[2]>(z)

Is well formed. It does not say

reinterpret_cast<cv T[2]>(z)

Is valid. To make you code compile you need to change the cast to cast to a reference which gives you

cfunc(reinterpret_cast<float(&)[2]>(z));

Live Example

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Ahhhhh me misinterpreting what the round parentheses meant! ` cfunc(*reinterpret_cast(z));` compiles (note the de-referencing after the cast, if the C library expects a single complex by value as in my test case). Your answer also explains why there is the mention of z being an lvalue in the standard. – Mark K Cowan May 25 '17 at 12:17
  • 1
    @MarkKCowan Yep. This requires it be an lvaule so you can bind to the reference. – NathanOliver May 25 '17 at 12:19
  • 1
    How would I pass a `std::complex *` to a C function which takes `complex T *`? – Mark K Cowan May 25 '17 at 12:45
  • @MarkKCowan Should be able to use: http://coliru.stacked-crooked.com/a/a598181914271d44 – NathanOliver May 25 '17 at 12:52
  • That works if the function takes `float(*)[2]` argument, but for a (C) function which takes pointer-to-complex, it doesn't seem to work: http://coliru.stacked-crooked.com/a/8a08b9e918722da5 – Mark K Cowan May 25 '17 at 14:30
  • @MarkKCowan I have very little experience with C. I'm not sure how you will be able to cast it as I do not understand the C type. – NathanOliver May 25 '17 at 14:33
  • Ok. Currently, I have proxy C++ functions which internally declare `extern ret_type c_function(args.... )` where the pointer-to-complex types are replaced with void pointers, and the proxy function calls this using casts. It works, but it's just ugly/WET and I was hoping that I'd been missing something obvious. – Mark K Cowan May 25 '17 at 14:36