|
無料版のVisual Studio 2019 Communityを使用してWindows版十進BASIC上で動くDLLファイルを作成します。
(他のバージョンでも操作は同じです)
https://visualstudio.microsoft.com/ja/downloads/
https://qiita.com/Gaccho/items/1409c27216a67014a024
但し、VC++ 2019のIDEは使用せず、テキストエディタ(メモ帳等)でc/cppソースファイルを記述します。
十進BASICはwin32アプリなのでx86用のコマンドプロンプトを使用します。
スタートメニューからVC++2019のx86 native tools コマンドプロンプト又はx64_x86 cross tools コマンドプロンプト
を起動します。
起動直後はカレントフォルダがCドライブのプログラムフォルダになっています。
cl /?と打ち込むとclコマンドのヘルプが表示されます。
これでコンパイル可能ですが、プログラムフォルダは書き込み禁止のためコンパイルに失敗します。
pathが既に通っていますので別フォルダに移動してから作業始めてください。
c/c++で十進BASICから呼び出す関数には
cソースでは__declspec(dllexport) をつけて、
cppソースではextern "C" __declspec(dllexport)をつけて記述します。
--------------------------------------------------------------------
sample.cpp
extern "C" __declspec(dllexport) int test(int a,int b)
{
int c;
c=a+b;
return c;
}
これをコンパイルしてDLLファイルを作成するには
cl /LD sample.cpp
と打ち込みます。/LDはdllファイルを作成するためのオプションです。
オプション /O2 や /Ox /Ot等で最適化を行います。
/arch:AVX 等のオプションをつけるとsimd命令のAVX等を利用できます。(※CPUが対応していること)
cl /LD /O2 /Ox /Ot /Qpar /EHsc /Gd /arch:AVX sample.cpp
BASIC側では次のようにします。
LET X=2147483647
LET Y=1
PRINT TEST(X,Y);MOD(X+Y+2^31,2^32)-2^31
END
EXTERNAL FUNCTION TEST(X,Y)
ASSIGN "sample.dll","test"
END FUNCTION
int型の引数はBASIC側ではそのまま数値変数としてDLLへ渡します。
整数型をreturnで受け取るのでFUNCTIONで定義します。
unsignedをつけることもできます。
--------------------------------------------------------------------
sample.cpp
extern "C" __declspec(dllexport) unsigned int test(unsigned int a,unsigned int b)
{
int c;
c=a*b;
return c;
}
LET X=-3.2354885
LET Y=5.657321
LET X=INT(X+.5)
LET Y=INT(Y+.5)
PRINT TEST(X,Y);X*Y;
LET X=MOD(X+2^32,2^32)
LET Y=MOD(Y+2^32,2^32)
LET Z=MOD(X*Y+2^32,2^32)-2^32
PRINT Z
END
EXTERNAL FUNCTION TEST(X,Y)
ASSIGN "sample.dll","test"
END FUNCTION
実数値を与えていますが、四捨五入されるようです。
unsigned int型のはずですが、-18と表示されました。
実数値を渡すときは
--------------------------------------------------------------------
sample.cpp
extern "C" __declspec(dllexport) double test(double *a,double *b)
{
double c;
c=*a * *b;
return c;
}
LET X=-3.2354885
LET Y=5.657321
LET X$=PACKDBL$(X)
LET Y$=PACKDBL$(Y)
PRINT TEST(X$,Y$);X*Y
END
EXTERNAL FUNCTION TEST(X$,Y$)
ASSIGN "sample.dll","test",FPU
END FUNCTION
double型をdllへ渡すときはPACKDBL$使ってdouble型バイナリーを渡します。
double型を受け取るのでBASIC側ではFUNCTIONで定義してASSIGN文にFPUをつけます。
なお、return文で戻値をBASIC側で受け取れるのはint型とdouble型のみのようです。
double型の1次元配列を渡すときは
--------------------------------------------------------------------
sample.cpp
extern "C" __declspec(dllexport) double sum(int n,double *a)
{
double s=0.0;
int i;
for(i=0;i<n;i++) s+=a[i];
return s;
}
OPTION CHARACTER BYTE
LET N=100
DIM A(N)
FOR I=1 TO N
LET A(I)=RND*100
NEXT
LET X$=REPEAT$(" ",8*N)
FOR I=0 TO N-1
LET X$(8*I+1:8*I+8)=PACKDBL$(A(I+1))
NEXT I
PRINT TEST(N,X$)
END
EXTERNAL FUNCTION TEST(N,X$)
OPTION CHARACTER BYTE
ASSIGN "sample.dll","sum",FPU
END FUNCTION
extern "C" __declspec(dllexport) double sum(int n,double a[])と書いても同じです。
OPTION CHARACTER BYTEを忘れずに書いてください。
REPEAT$でバファーを確保して部分文字列で書き込んでいます。
double型は8バイトなのでそれに個数分のバッファーを用意します。
変数Nで個数を与えています。
なお、2次元配列を渡したい時は、2次元配列を1次元配列化して渡します。
--------------------------------------------------------------------
sample.cpp
extern "C" __declspec(dllexport) double sum(int n,int m,double *a)
{
double s=0.0;
int i,j;
for(i=0; i<n; i++)
for(j=0; j<m; j++)
s+=a[i*m+j];
return s;
}
OPTION CHARACTER BYTE
LET N=100
LET M=50
DIM A(N,M)
FOR I=1 TO N
FOR J=1 TO M
LET A(I,J)=RND*100
NEXT J
NEXT I
LET X$=REPEAT$(" ",8*N*M)
FOR I=0 TO N-1
FOR J=0 TO M-1
LET X$(8*(I*M+J)+1:8*(I*M+J)+8)=PACKDBL$(A(I+1,J+1))
NEXT J
NEXT I
PRINT TEST(N,M,X$)
END
EXTERNAL FUNCTION TEST(N,M,X$)
OPTION CHARACTER BYTE
ASSIGN "sample.dll","sum",FPU
END FUNCTION
DLLから2個以上のdouble型を受け取りたい時は
--------------------------------------------------------------------
sample.cpp
#include <cmath>
extern "C" __declspec(dllexport) void csin(double *x,double *y,double *re,double *im)
{
*re=sin(*x)*cosh(*y);
*im=cos(*x)*sinh(*y);
}
LET X$=PACKDBL$(PI/4)
LET Y$=PACKDBL$(2)
LET R$=REPEAT$(" ",8)
LET I$=REPEAT$(" ",8)
CALL TEST(X$,Y$,R$,I$)
PRINT UNPACKDBL(R$);UNPACKDBL(I$)
END
EXTERNAL SUB TEST(X$,Y$,R$,I$)
ASSIGN "sample.dll","csin"
END SUB
BASIC側ではR$,I$で受け取るのでREPEAT$でバファーを確保し
SUBとして定義します。
double型の1次元配列も受け渡しできます。
--------------------------------------------------------------------
dft.cpp
#include <cmath>
#include <omp.h>
#define PI 3.14159265358979323846
using namespace std;
extern "C" __declspec(dllexport) void dft(int m, double *xr, double *xi,double *yr,double *yi,int sw)
{
double p,rr,ii;
int i,j;
p = 2.0*PI/(double)m;
if(sw!=0) p=-p;
#pragma omp parallel for private(i,j)
for (j=0; j<m; j++) {
rr=0.0;
ii=0.0;
for (i=0; i<m; i++) {
rr += xr[i] * cos(p * j * i) - xi[i] * sin(p * j * i);
ii += xr[i] * sin(p * j * i) + xi[i] * cos(p * j * i);
}
yr[j]=rr;
yi[j]=ii;
}
}
OPTION CHARACTER BYTE
RANDOMIZE
LET M=100
DIM XR(M),XI(M),YR(M),YI(M)
FOR I=1 TO M
LET XR(I)=INT(RND*1000)
LET XI(I)=0
NEXT I
LET XR$=REPEAT$(" ",M*8)
LET XI$=REPEAT$(" ",M*8)
LET YR$=REPEAT$(" ",M*8)
LET YI$=REPEAT$(" ",M*8)
FOR I=0 TO M-1
LET XR$(8*I+1:8*I+8)=PACKDBL$(XR(I+1))
LET XI$(8*I+1:8*I+8)=PACKDBL$(XI(I+1))
NEXT I
CALL DFT(M,XR$,XI$,YR$,YI$,0) !'dft
CALL DFT(M,YR$,YI$,XR$,XI$,1) !'idft
FOR I=0 TO M-1
PRINT INT(UNPACKDBL(XR$(8*I+1:8*I+8))/M+.5);XR(I+1)
NEXT I
END
EXTERNAL SUB DFT(M,XR$,XI$,YR$,YI$,SW)
OPTION CHARACTER BYTE
ASSIGN "dft.dll","dft"
END SUB
double型で個数分のバッファーを用意します。
int型も受け渡しできます。
--------------------------------------------------------------------
sample.cpp
extern "C" __declspec(dllexport) void test(int *a,int *b,int *c)
{
*c=*a * *b;
}
LET X=123456
LET Y=456789
LET X$=DWORD$(123456)
LET Y$=DWORD$(456789)
LET Z$=REPEAT$(" ",4)
CALL TEST(X$,Y$,Z$)
PRINT INT32(Z$);X*Y;MOD(X*Y,2^32)
END
EXTERNAL SUB TEST(X$,Y$,Z$)
ASSIGN "sample.dll","test"
END SUB
EXTERNAL FUNCTION INT32(S$)
OPTION CHARACTER BYTE
FOR I=1 TO 4
LET N=N+256^(I-1)*ORD(S$(I:I))
NEXT I
LET INT32=MOD(N+2^31,2^32)-2^31
END FUNCTION
DWORD$でint型バイナリーを渡します。
int型は4バイトです。
受け取ったint型バイナリーはINT32関数で整数値にします。
32bit整数の範囲を超える部分は無視されます。
1000桁モードや有理数モードを使用して
64bit整数型を受け渡しできます。
unsignedもつけられます。
--------------------------------------------------------------------
sample.cpp
extern "C" __declspec(dllexport) void test(int n,long long int *a,long long int *s)
{
*s=0;
for(int i=0; i<n; i++) *s+=a[i];
}
OPTION ARITHMETIC RATIONAL
OPTION CHARACTER BYTE
RANDOMIZE
LET N=100
DIM A(N)
LET N$=REPEAT$(" ",8*N)
LET ANS$=REPEAT$(" ",8)
FOR I=0 TO N-1
LET A(I+1)=INT(RND*2^63)
LET S=MOD(S+A(I+1),2^63)
LET N$(8*I+1:8*I+8)=LWORD$(A(I+1))
NEXT I
CALL TEST(N,N$,ANS$)
PRINT INT64(ANS$)
PRINT S
END
EXTERNAL SUB TEST(NUM,N$,ANS$)
OPTION ARITHMETIC RATIONAL
OPTION CHARACTER BYTE
ASSIGN "sample.dll","test"
END SUB
EXTERNAL FUNCTION LWORD$(N)
OPTION ARITHMETIC RATIONAL
OPTION CHARACTER BYTE
DIM A$(8)
IF N<0 THEN LET N=N+2^64
FOR I=1 TO 8
LET A$(I)=CHR$(MOD(N,256))
LET N=INT(N/256)
NEXT I
LET LWORD$=A$(1)&A$(2)&A$(3)&A$(4)&A$(5)&A$(6)&A$(7)&A$(8)
END FUNCTION
EXTERNAL FUNCTION INT64(S$)
OPTION ARITHMETIC RATIONAL
OPTION CHARACTER BYTE
FOR I=1 TO 8
LET N=N+256^(I-1)*ORD(S$(I:I))
NEXT I
IF N>2^63 THEN LET N=N-2^63
LET INT64=N
END FUNCTION
long long int型は64bitの整数で8バイトです。
それに個数分のバッファーを用意して1次元配列を渡します。
受け取りはlong long int型1つなので8バイト分のバッファーを用意しておきます。
long long int型はBASIC側ではサポートされていないので自前で関数を用意します。
複素数型でも受け渡しできるようです。
--------------------------------------------------------------------
cexp.cpp
#include <complex>
using namespace std;
extern "C" __declspec(dllexport) void cexp(complex<double>*x,complex<double>*y)
{
*y=exp(*x);
}
OPTION ARITHMETIC COMPLEX
OPTION CHARACTER BYTE
LET X=COMPLEX(.5,.5)
LET XX$=REPEAT$(" ",16)
LET YY$=REPEAT$(" ",16)
LET XX$(1:8)=PACKDBL$(RE(X))
LET XX$(9:16)=PACKDBL$(IM(X))
CALL CEXP(XX$,YY$)
PRINT UNPACKDBL(YY$(1:8));UNPACKDBL(YY$(9:16))
PRINT EXP(X)
END
EXTERNAL SUB CEXP(XX$,YY$)
OPTION ARITHMETIC COMPLEX
OPTION CHARACTER BYTE
ASSIGN "j:\src\cexp.dll","cexp"
END sub
complex型はdouble型2個使用するため、バッファーサイズは
倍の16バイトになります。
double型の引数で受け渡しをする時は
--------------------------------------------------------------------
csin.cpp
#include <complex>
using namespace std;
extern "C" __declspec(dllexport) void csin(double *xr,double *xi,double *yr,double *yi)
{
complex<double> x,y;
x=complex<double>(*xr,*xi);
y=sin(x);
*yr=y.real();
*yi=y.imag();
}
char型でも数値を渡せます。
--------------------------------------------------------------------
sample.cpp
#include <cstdlib>
using namespace std;
extern "C" __declspec(dllexport) double test(char *a)
{
double k;
k=atof(a);
// k=strtod(a,NULL);
return k;
}
OPTION CHARACTER BYTE
LET A=SQR(3)
PRINT TEST(STR$(A));A
END
EXTERNAL FUNCTION TEST(N$)
OPTION CHARACTER BYTE
ASSIGN "sample.dll","test",FPU
END FUNCTION
数値変数からはSTR$を使って数値文字列を渡します。
指数表現(1E+5など)も可能です。
有理数モードや複素数モードでは注意が必要です。
有理数モードでは
LET A=1/3
PRINT STR$(A)
END
複素数モードでは
LET A=COMPLEX(1,1)
PRINT STR$(A)
END
"/"や"("などの文字記号が入る場合があり、そのままDLLへ渡すと内部エラーを引き起こします。
int型ならatoi関数、long型ではatol関数で数値に変換できます。
|
|