C++でライブラリー(DLL)を作り、pythonなどから利用する方法について連載します。
1. C++でライブラリーを作る Linux編
2. pythonからC++のライブラリーを利用する Linux編
3. C++でライブラリー(DLL)を作る Windows編
4. pythonからC++のライブラリー(DLL)を利用する Windows編
5. C#からC++のライブラリー(DLL)を利用する
6. python・C#とC++間で複素数を受け渡す
このページでは信号処理などで利用されている複素数のやりとりについて説明します。
ライブラリー(DLL)
C++で複素数を利用する場合は次のヘッダーファイルをインクルードします。
#include< complex >
変数の型は std::complex< double > です。
サンプルコードでは、絶対値(abs)と偏角(arg)を入力し複素数を出力する関数と、複素数を入力し絶対値と偏角を出力する関数を用意しました。ついでに角度の度とラジアンを変換する関数も用意しました。
#include <windows.h>
#include <stdio.h>
#include <string.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <complex>
#define DLLAPI extern "C" __declspec(dllexport)
#include "LocalLib.h"
#pragma warning(disable:4996)
class CLocalClass {
public:
CLocalClass(void);
virtual ~CLocalClass(void);
int GetComplexFromAbsArg(std::complex<double> *cpOut, double dInAbs, double dInArg);
int GetAbsArgFromComplex(double *dpOutAbs, double *dpOutArg, std::complex<double> *cpIn);
double RadToDeg(double dInRad);
double DegToRad(double dInDeg);
private:
};
CLocalClass *CP;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
switch(ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
CP = new CLocalClass;
break;
case DLL_PROCESS_DETACH:
delete CP;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
default:
break;
}
return TRUE;
}
DLLAPI int GetComplexFromAbsArg(std::complex<double> *cpOut, double dInAbs, double dInArg)
{
int nRet = (CP->GetComplexFromAbsArg)(cpOut, dInAbs, dInArg);
return(nRet);
}
DLLAPI int GetAbsArgFromComplex(double *dpOutAbs, double *dpOutArg, std::complex<double> *cpIn)
{
int nRet = (CP->GetAbsArgFromComplex)(dpOutAbs, dpOutArg, cpIn);
return(nRet);
}
DLLAPI double RadToDeg(double dInRad)
{
double dRet = (CP->RadToDeg)(dInRad);
return(dRet);
}
DLLAPI double DegToRad(double dInDeg)
{
double dRet = (CP->DegToRad)(dInDeg);
return(dRet);
}
//================================================================================
// CLocalClass
//================================================================================
CLocalClass::CLocalClass(void)
{
printf("start DLL\n");
}
CLocalClass::~CLocalClass(void)
{
printf("end DLL\n");
}
int CLocalClass::GetComplexFromAbsArg(std::complex<double> *cpOut, double dInAbs, double dInArg)
{
double dReal = dInAbs * std::cos(dInArg);
double dImag = dInAbs * std::sin(dInArg);
(*cpOut) = std::complex<double>(dReal, dImag);
return(0);
}
int CLocalClass::GetAbsArgFromComplex(double *dpOutAbs, double *dpOutArg, std::complex<double> *cpIn)
{
(*dpOutAbs) = std::abs(*cpIn);
(*dpOutArg) = std::arg(*cpIn);
return(0);
}
double CLocalClass::RadToDeg(double dInRad)
{
double dDeg = (dInRad * 180.0) / M_PI;
return(dDeg);
}
double CLocalClass::DegToRad(double dInDeg)
{
double dRad = (dInDeg * M_PI)/ 180.0;
return(dRad);
}
#ifndef DLLAPI
#define DLLAPI extern "C" __declspec(dllimport)
#endif
DLLAPI int GetComplexFromAbsArg(std::complex<double> *cpOut, double dInAbs, double dInArg);
DLLAPI int GetAbsArgFromComplex(double *dpOutAbs, double *dpOutArg, std::complex<double> *cpIn);
DLLAPI double RadToDeg(double dInRad);
DLLAPI double DegToRad(double dInDeg);
C++からライブラリー(DLL)を利用する
まず呼び出し側もC++で作って、ライブラリー(DLL)の動作確認をします。ヘッダーファイルはライブラリーと同じもの(LocalLib.h)を使用します。DLL作成時に同時に出来たインポートライブラリー(CallLib.lib)も使用します。
前半でライブラリー(DLL)から呼び出し側へ複素数を渡します。絶対値=2、偏角=30度を入力すると相当の複素数が出力されます。高さ(imag)=1、斜辺(abs)=2、底辺(real)=ルート3、角度(arg)=30度と三平方の定理でおなじみの値が表示されます。
後半で呼び出し側からライブラリー(DLL)へ複素数を渡します。前半結果の複素数の実部(real)と虚部(imag)を入れ替えた複素数を入力すると相当の絶対値と偏角が出力されます。斜辺(abs)は同じままで、底辺(real)と高さ(imag)が入れ替わり角度(arg)=60度と表示されます。
#include <stdio.h>
#include <string.h>
#include <complex>
#include "LocalLib.h"
#pragma comment(lib, "LocalLib.lib")
#pragma warning(disable:4996)
static void PrintResult(std::complex<double> cplx, double dAbs, double dArg);
int main(int argc, char *argv[])
{
double dAbs, dArg;
std::complex<double> cplx1, cplx2;
dAbs = 2.0;
dArg = DegToRad(30.0);
GetComplexFromAbsArg(&cplx1, dAbs, dArg);
PrintResult(cplx1, dAbs, dArg);
//----------------
cplx2 = std::complex<double> ((cplx1.imag()), (cplx1.real()));
GetAbsArgFromComplex(&dAbs, &dArg, &cplx2);
PrintResult(cplx2, dAbs, dArg);
return(0);
}
static void PrintResult(std::complex<double> cplx, double dAbs, double dArg)
{
double dReal = cplx.real();
double dImag = cplx.imag();
double dDeg = RadToDeg(dArg);
printf("abs=%.3lf arg=%.3lfdeg real=%.6lf imag=%.6lf\n",
dAbs, dDeg, dReal, dImag );
}
D:\CallLibCpp\x64\Release>CallLib.exe
start DLL
abs=2.000 arg=30.000deg real=1.732051 imag=1.000000
abs=2.000 arg=60.000deg real=1.000000 imag=1.732051
end DLL
pythonからライブラリー(DLL)を利用する
python 3.14 からは ctypes で複素数(c_double_complexなど)もサポートされるそうです。c_intやc_doubleなど他の変数と同様に複素数も扱うことが出来て便利になります。
あいにく本稿執筆時点の筆者の環境は3.12で c_double_complex を試すことが出来ません。代わりにnumpyを利用します。dtype='complex128' で C++ の std::complex< double > と互換性のある変数を作ることが出来ます。ポインターは ctypes.data_as(ctypes.c_void_p) で利用可能です。
コードの内容はC++と同一です。前半でDLLからpythonへ、後半でpythonからDLLへ複素数を渡しています。詳細は上述の「C++からライブラリー(DLL)を利用する」をご覧下さい。
import os
import sys
import ctypes
import numpy
def main(argv):
#----------------
global LLib
#----------------
path = os.getcwd()
sFnDll = path + '\\LocalLib.dll'
LLib = ctypes.cdll.LoadLibrary(sFnDll)
#================================================================
cplx1 = numpy.zeros(1, dtype='complex128')
dAbs = ctypes.c_double(2.0)
dArg = DegToRad(30.0)
GetComplexFromAbsArg((cplx1.ctypes.data_as(ctypes.c_void_p)), dAbs, dArg)
PrintResult(cplx1, dAbs, dArg)
#================================================================
cplx2 = numpy.zeros(1, dtype='complex128')
cplx2.real = cplx1.imag
cplx2.imag = cplx1.real
GetAbsArgFromComplex(ctypes.pointer(dAbs), ctypes.pointer(dArg), (cplx2.ctypes.data_as(ctypes.c_void_p)))
PrintResult(cplx2, dAbs, dArg)
def PrintResult(cplx, dAbs, dArg):
Deg = RadToDeg(dArg.value)
print('abs=%.3lf arg=%.3lfdeg real=%.6lf imag=%.6lf' %\
( (dAbs.value), (Deg.value), (cplx[0].real), (cplx[0].imag) ) )
def DegToRad(dInDeg):
LLib.DegToRad.argtype = ctypes.c_double
LLib.DegToRad.restype = ctypes.c_double
dIn = ctypes.c_double(dInDeg)
#--------------------------------
dRad = LLib.DegToRad(dIn)
#--------------------------------
dRet = ctypes.c_double(dRad)
return dRet
def RadToDeg(dInRad):
LLib.RadToDeg.argtype = ctypes.c_double
LLib.RadToDeg.restype = ctypes.c_double
dIn = ctypes.c_double(dInRad)
#--------------------------------
dDeg = LLib.RadToDeg(dIn)
#--------------------------------
dRet = ctypes.c_double(dDeg)
return dRet
def GetComplexFromAbsArg(cplx, Abs, Arg):
LLib.GetComplexFromAbsArg.argtypes = (ctypes.c_void_p, ctypes.c_double, ctypes.c_double)
LLib.GetComplexFromAbsArg.restype = ctypes.c_int
nRet = LLib.GetComplexFromAbsArg(cplx, Abs, Arg)
return nRet
def GetAbsArgFromComplex(Abs, dArg, cplx):
LLib.GetAbsArgFromComplex.argtypes = (ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p)
LLib.GetAbsArgFromComplex.restype = ctypes.c_int
nRet = LLib.GetAbsArgFromComplex(Abs, dArg, cplx)
return nRet
if __name__ == '__main__':
main(sys.argv)
D:\CallLibPy>python CallLib.py
start DLL
abs=2.000 arg=30.000deg real=1.732051 imag=1.000000
abs=2.000 arg=60.000deg real=1.000000 imag=1.732051
end DLL
C#からライブラリー(DLL)を利用する
C#で複素数を利用する場合は、プロジェクト作成後にプロジェクト名を右クリック > 追加 > 参照 をクリックすると表示される参照マネージャーにて System.Numerics をチェックしてOKボタンを押して下さい。

複素数の型は System.Numerics.Complex です。他の型の変数と同様にrefで変数のポインターが利用出来ます。
コードの内容はC++やpythonと同一です。前半でDLLからC#へ、後半でC#からDLLへ複素数を渡しています。詳細は上述の「C++からライブラリー(DLL)を利用する」をご覧下さい。
using System.Runtime.InteropServices;
namespace CallLibCs
{
internal class Program
{
[DllImport("LocalLib.dll")]
static extern int GetComplexFromAbsArg(ref System.Numerics.Complex cpOut, double dInAbs, double dInArg);
[DllImport("LocalLib.dll")]
static extern int GetAbsArgFromComplex(ref double dpOutAbs, ref double dpOutArg, ref System.Numerics.Complex cpIn);
[DllImport("LocalLib.dll")]
static extern double RadToDeg(double dInRad);
[DllImport("LocalLib.dll")]
static extern double DegToRad(double dInDeg);
static void Main(string[] args)
{
double dAbs, dArg;
dAbs = 2.0;
dArg = DegToRad(30.0);
System.Numerics.Complex cplx1 = new System.Numerics.Complex(0,0);
GetComplexFromAbsArg(ref cplx1, dAbs, dArg);
PrintResult(cplx1, dAbs, dArg);
//----------------
System.Numerics.Complex cplx2 = new System.Numerics.Complex((cplx1.Imaginary), (cplx1.Real));
GetAbsArgFromComplex(ref dAbs, ref dArg, ref cplx2);
PrintResult(cplx2, dAbs, dArg);
}
static void PrintResult(System.Numerics.Complex cplx, double dAbs, double dArg)
{
double dReal = cplx.Real;
double dImag = cplx.Imaginary;
double dDeg = RadToDeg(dArg);
System.Console.Write("abs={0:00.000} arg={1:00.000}deg real={2:0.000000} imag={3:0.000000}\n",
dAbs, dDeg, (cplx.Real), (cplx.Imaginary) );
}
}
}
D:\CallLibCs\bin\Release>CallLibCs.exe
start DLL
abs=02.000 arg=30.000deg real=1.732051 imag=1.000000
abs=02.000 arg=60.000deg real=1.000000 imag=1.732051
end DLL
Linuxでの動作例
ここまではWindowsで説明しましたが、Linuxでも同じことが出来ます。
ライブラリーのコードはこちらです。
#include <stdio.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <complex>
#include "libLocal.h"
class CLocalClass {
public:
CLocalClass(void);
virtual ~CLocalClass(void);
int GetComplexFromAbsArg(std::complex<double> *cpOut, double dInAbs, double dInArg);
int GetAbsArgFromComplex(double *dpOutAbs, double *dpOutArg, std::complex<double> *cpIn);
double RadToDeg(double dInRad);
double DegToRad(double dInDeg);
private:
};
CLocalClass *CP;
void __attribute__ ((constructor)) LibAttach(void)
{
CP = new CLocalClass;
}
void __attribute__ ((destructor)) LibDetach(void) {
delete CP;
}
int GetComplexFromAbsArg(std::complex<double> *cpOut, double dInAbs, double dInArg)
{
int nRet = (CP->GetComplexFromAbsArg)(cpOut, dInAbs, dInArg);
return(nRet);
}
int GetAbsArgFromComplex(double *dpOutAbs, double *dpOutArg, std::complex<double> *cpIn)
{
int nRet = (CP->GetAbsArgFromComplex)(dpOutAbs, dpOutArg, cpIn);
return(nRet);
}
double RadToDeg(double dInRad)
{
double dRet = (CP->RadToDeg)(dInRad);
return(dRet);
}
double DegToRad(double dInDeg)
{
double dRet = (CP->DegToRad)(dInDeg);
return(dRet);
}
//================================================================================
// CLocalClass
//================================================================================
CLocalClass::CLocalClass(void)
{
printf("start DLL\n");
}
CLocalClass::~CLocalClass(void)
{
printf("end DLL\n");
}
int CLocalClass::GetComplexFromAbsArg(std::complex<double> *cpOut, double dInAbs, double dInArg)
{
double dReal = dInAbs * std::cos(dInArg);
double dImag = dInAbs * std::sin(dInArg);
(*cpOut) = std::complex<double>(dReal, dImag);
return(0);
}
int CLocalClass::GetAbsArgFromComplex(double *dpOutAbs, double *dpOutArg, std::complex<double> *cpIn)
{
(*dpOutAbs) = std::abs(*cpIn);
(*dpOutArg) = std::arg(*cpIn);
return(0);
}
double CLocalClass::RadToDeg(double dInRad)
{
double dDeg = (dInRad * 180.0) / M_PI;
return(dDeg);
}
double CLocalClass::DegToRad(double dInDeg)
{
double dRad = (dInDeg * M_PI)/ 180.0;
return(dRad);
}
extern "C" {
int GetComplexFromAbsArg(std::complex<double> *cpOut, double dInAbs, double dInArg);
int GetAbsArgFromComplex(double *dpOutAbs, double *dpOutArg, std::complex<double> *cpIn);
double RadToDeg(double dInRad);
double DegToRad(double dInDeg);
}
ライブラリーのビルド例です。
g++ -shared -fPIC -o libLocal.so libLocal.cpp
呼び出し側をC++で書いた例です。ヘッダーファイルはライブラリーと同じものを利用します。 他の例と同様に前半でライブラリーから呼び出し側へ、後半で呼び出し側からライブラリーへ複素数を渡しています。
#include <stdio.h>
#include <string.h>
#include <complex>
#include "libLocal.h"
static void PrintResult(std::complex<double> cplx, double dAbs, double dArg);
int main(int argc, char *argv[])
{
double dAbs, dArg;
std::complex<double> cplx1, cplx2;
dAbs = 2.0;
dArg = DegToRad(30.0);
GetComplexFromAbsArg(&cplx1, dAbs, dArg);
PrintResult(cplx1, dAbs, dArg);
//----------------
cplx2 = std::complex<double> ((cplx1.imag()), (cplx1.real()));
GetAbsArgFromComplex(&dAbs, &dArg, &cplx2);
PrintResult(cplx2, dAbs, dArg);
return(0);
}
static void PrintResult(std::complex<double> cplx, double dAbs, double dArg)
{
double dReal = cplx.real();
double dImag = cplx.imag();
double dDeg = RadToDeg(dArg);
printf("abs=%.3lf arg=%.3lfdeg real=%.6lf imag=%.6lf\n",
dAbs, dDeg, dReal, dImag );
}
呼び出し側のビルド例です。
g++ -o CallC.o -c CallC.cpp
g++ -L./ -Wl,-rpath=./ -o CallC CallC.o -lLocal
実行結果はこちらです。
$ ./CallC
start DLL
abs=2.000 arg=30.000deg real=1.732051 imag=1.000000
abs=2.000 arg=60.000deg real=1.000000 imag=1.732051
end DLL
呼び出し側をpythonで書いた例です。他の例と同様に前半でライブラリーからpythonへ、後半でpythonからライブラリーへ複素数を渡しています。
import os
import sys
import ctypes
import numpy
def main(argv):
#----------------
global LLib
#----------------
LLib = ctypes.cdll.LoadLibrary("./libLocal.so")
#================================================================
cplx1 = numpy.zeros(1, dtype='complex128')
dAbs = ctypes.c_double(2.0)
dArg = DegToRad(30.0)
GetComplexFromAbsArg((cplx1.ctypes.data_as(ctypes.c_void_p)), dAbs, dArg)
PrintResult(cplx1, dAbs, dArg)
#================================================================
cplx2 = numpy.zeros(1, dtype='complex128')
cplx2.real = cplx1.imag
cplx2.imag = cplx1.real
GetAbsArgFromComplex(ctypes.pointer(dAbs), ctypes.pointer(dArg), (cplx2.ctypes.data_as(ctypes.c_void_p)))
PrintResult(cplx2, dAbs, dArg)
def PrintResult(cplx, dAbs, dArg):
Deg = RadToDeg(dArg.value)
print('abs=%.3lf arg=%.3lfdeg real=%.6lf imag=%.6lf' %\
( (dAbs.value), (Deg.value), (cplx[0].real), (cplx[0].imag) ) )
def DegToRad(dInDeg):
LLib.DegToRad.argtype = ctypes.c_double
LLib.DegToRad.restype = ctypes.c_double
dIn = ctypes.c_double(dInDeg)
#--------------------------------
dRad = LLib.DegToRad(dIn)
#--------------------------------
dRet = ctypes.c_double(dRad)
return dRet
def RadToDeg(dInRad):
LLib.RadToDeg.argtype = ctypes.c_double
LLib.RadToDeg.restype = ctypes.c_double
dIn = ctypes.c_double(dInRad)
#--------------------------------
dDeg = LLib.RadToDeg(dIn)
#--------------------------------
dRet = ctypes.c_double(dDeg)
return dRet
def GetComplexFromAbsArg(cplx, Abs, Arg):
LLib.GetComplexFromAbsArg.argtypes = (ctypes.c_void_p, ctypes.c_double, ctypes.c_double)
LLib.GetComplexFromAbsArg.restype = ctypes.c_int
nRet = LLib.GetComplexFromAbsArg(cplx, Abs, Arg)
return nRet
def GetAbsArgFromComplex(Abs, dArg, cplx):
LLib.GetAbsArgFromComplex.argtypes = (ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p)
LLib.GetAbsArgFromComplex.restype = ctypes.c_int
nRet = LLib.GetAbsArgFromComplex(Abs, dArg, cplx)
return nRet
if __name__ == '__main__':
main(sys.argv)
実行結果はこちらです。
$ python CallLib.py
start DLL
abs=2.000 arg=30.000deg real=1.732051 imag=1.000000
abs=2.000 arg=60.000deg real=1.000000 imag=1.732051
end DLL