random kludges

notes for my future self

C++ Complex

C complex numbers are a bit tricky in C++.

My project is C++ but the nifty library I want to use is C, and it uses C99 complex numbers. There seems to be a few problems with this.

complex.h

Ok, project.cc includes the C library header, and the result:

$ g++ project.cc -c 2>&1|wc -l
6974

That’s about 7000 lines of errors…

Errors like

/usr/include/c++/5/cmath:87:16: error: conflicting declaration of C function ‘constexpr float std::abs(float)’
/usr/include/c++/5/cmath:81:3: note: previous declaration ‘constexpr double std::abs(double)’

Right. The system provided cmath doesn’t compile? This is due to the (GNU) implementation of complex.h. When the project compiles the library header as C++, the implementation of complex.h boils down to a #include <complex>. And because the library is C, its header is wrapped with extern "C", and the function overloading in complex breaks.

To demonstrate - compling this file as C++ results in the insane amount of gcc errors.

extern "C" {
#include <complex.h>
}

But since the library is already compiled separately, those name clashes don’t really matter - those functions are not called from the extern "C" space. And it does not matter if the library uses those functions internally - the C compliler has already figured out what to call.

So the problem is including of the C++ complex header in the extern "C" scope. That is easily fixed by

#include <complex>
extern "C" {
#include "library_header_that_includes_complex.h"
}

Here the first #include short-circuits including <complex> inside extern "C" as there are - per good coding standards - #pragma once type guards in it. Problem solved :)

And onwards to the next problem :(

The complex keyword

Now g++ complains about

error: expected ‘,’ or ‘...’ before ‘*’ token
void fft(int* x, double complex* X, uint16_t N);
                               ^

This in turn turns out to be due to the double complex. complex is a keyword in C99 but a template in C++, so the compiler does not detect double complex as a type.

A work-aroud for this is to modify the C header to use double _Complex instead. But this seems to be an optional extension of the C++ compiler. And probably for the C compiler as well. Oh well, least gcc and g++ both understand it.

Best perhaps just to define a custom cdouble type, like Potatoswatter suggest on Stackoverflow. Turns out C++ standards require std::complex<double> to be binary compatible with double complex.

Complex arithmetics

After this much changes I got a bit sceptical if everything still works. So I tried to compile the C library’s unit tests as C++. This hits yet another snag:

cdouble a = 2 + 2 * I;

is valid C, but not C++. This has two parts. For one, I is not C++. This can be fixed by defining a I in the C++ context too as const cdouble I(0.0, 1.0); Now g++ complains with:

error: no match for ‘operator*’ (operand types are ‘int’ and ‘const cdouble {aka const std::complex<double>}’)
cdouble a = 2 + 2 * I;
                  ^

as operations like int * complex<T> are not defined. Again, Stackoverflow to the rescue. User tesch1 has nice opeator extension templates.

 

Putting it all together. Add this to a header that is #include‘d before #includeing the C library header.

// TODO: Extend the C headers with 
// typedef double complex cdouble;
// And edit out all instances of 'double complex' to read 'cdouble'

#include <complex>
typedef std::complex<double> cdouble;

const cdouble I(0.0, 1.0);

template <typename T>
struct identity_t { typedef T type; };
#define COMPLEX_OPS(OP)                                                 \
  template <typename _Tp>                                               \
  std::complex<_Tp>                                                     \
  operator OP(std::complex<_Tp> lhs, const typename identity_t<_Tp>::type & rhs) \
  {                                                                     \
    return lhs OP rhs;                                                  \
  }                                                                     \
  template <typename _Tp>                                               \
  std::complex<_Tp>                                                     \
  operator OP(const typename identity_t<_Tp>::type & lhs, const std::complex<_Tp> & rhs) \
  {                                                                     \
    return lhs OP rhs;                                                  \
  }
COMPLEX_OPS(+)
COMPLEX_OPS(-)
COMPLEX_OPS(*)
COMPLEX_OPS(/)
#undef COMPLEX_OPS

 

P.s. The insane amount of error messages can be short-circuited on gcc with -Wfatal-errors. That stops everything after the first error.

Home