supported by:
C
andC++
GNU
andClang
andMSVC
CUDA
andOpenMP
andMPI
Linux
andMacOS
andWindows
.
Our goal is to create a C++
library which handles complex numbers aliased as qcomp
, which can be used directly by both C++
and C
user codes.
The challenge is that while the C++
library is compiled with qcomp = std::complex<double>
(the type provided by <complex>
),
the C
user's code will understand qcomp
as a double complex
(or in MSVC
, a _Dcomplex
) (provided by <complex.h>
).
Our requirements are:
- the library backend and the user codes all use the same type alias;
qcomp
- the library backend is always compiled as
C++
by aC++
compiler - the library backend always understands
qcomp
asstd::complex<double>
(regardless of the user's language) - a
C++
user can passqcomp
's of underlying typestd::complex<double>
to/from the library - a
C
user can passqcomp
's of underlying typedouble complex
(or_Dcomplex
) to/from the library
This repo contains files:
src/types.both
which definesqcomp = std::complex<double>
(C++
type) orqcomp = double complex
(C
type), orqcomp = _Dcomplex
(MSVC'sC
type)src/core.cpp
which defines all backend functions using strictlyqcomp = std::complex<double>
(C++
type). It includes all functions which are agnostic toC
andC++
, and functions only callable directly byC++
.src/alts.cpp
definesC
-compatible wrappers for the functions incore.cpp
which are only directly callable byC++
.src/core.both
which declares only compiler-compatible functions to the parsing compiler. It definesC
-only wrappers ofsrc/alt.cpp
's C-compatible wrappers.main.cpp
which is an exampleC++
user's codemain.c
which is an exampleC
user's codecompile.sh
which compiles both user's codes
The proposed solution assumes that std::complex<double>
and double complex
have identical layouts.
- we first compile the backend in
C++
(whereinqcomp
is theC++
type) - we compile the user's
C++
code, trivially, whereinqcomp
is theC++
type - we compile the user's
C
code, whereinqcomp
is theC
type - both user codes parse
core.both
, whereinqcomp
is ambiguous types.both
is parsed by bothC++
andC
, which explicitly resolvesqcomp
to the compiler's native type- backend functions which pass or return
qcomp
by value can only be defined directly forC++
. We must provide an alternate return-by-pointer which aC
binary can safely call. These permittedly-name-mangled functions are replicated in code compiled only byC
, by wrapping the alternate function.
In essence:
- user's code uses the native type of their language (
C
vsC++
) - the backend always uses the
C++
type - the user's
C
code is "tricked" into believing the backend used theC
type - the
C
andC++
binaries can exchange pointers toqcomp
- the
C
andC++
binaries cannot exchangeqcomp
by value; soC++
defines functions withqcomp
in the signature only forC++
(name-mangling on), and provides alternateC
-friendly versions which exchange pointers. TheC
compiler additionally preparesC
-facing wrappers of these friendly versions, mimmicking theC++
interface
The full architecture resembles:
although it is easier understood if we remove the straightforward agnostic functions: