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
#include
ing 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.