0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C++でライブラリーを作る Linux編

0
Last updated at Posted at 2025-11-19

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++間で複素数を受け渡す

ライブラリー作成時のポイント

C++で作ったライブラリーをC++で利用する場合はライブラリーをC++で書いても支障ないのですが、pythonなど他言語から利用する場合はエクスポート関数をCの書式にする必要があります。LinuxでC++コンパイラー(g++)でビルドする場合、ヘッダーファイルでエクスポート関数を定義する箇所を extern "C" { } で囲むとソースコードは追加なしで良いようです。

WindowsではDLLの開始時や終了時の処理をdllmain()内の DLL_PROCESS_ATTACH や DLL_PROCESS_DETACHで実施することが出来ます。Linuxでも同じことを LibAttach() と LibDetach()で実施できます。

ライブラリーのサンプルコード

今回作ったライブラリーはC++で記述しました。ライブラリー起動時と終了時にクラスのインスタンスをnew/deleteします。Cのエクスポート関数内でC++クラスのメンバー関数を実行します。

4つのエクスポート関数を用意しました。

int LLibSum(int nInA, int nInB);
  整数変数を渡して受け取る例です。2つの引数の和を返します。

int LLibCalcX10(double *dpOut, double *dpIn, int nInLen);
  配列を渡して受け取る例です。実数データを10倍にします。

int LLibTxRxMessage(char *bpOut, char *bpIn, int nInLen);
  文字列を渡して受け取る例です。
  ライブラリーが受け取ったメッセージに
  ライブラリーのメッセージを加えて返します。

int LLibInOutStr(tagLibParameter *sp);
  構造体の受け渡し例です。
  利用側で入力した2つの値の和
  および日付時間情報の文字列を返します。

LocalLib.cpp
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "LocalLib.h"

class CLocalClass {
public:
	CLocalClass(void);
	virtual ~CLocalClass(void);
	int Sum(int nInA, int nInB);
	int CalcX10(double *dpOut, double *dpIn, int nInLen);
	int TxRxMessage(char *bpOut, char *bpIn, int nInLen);
	int InOutStr(tagLibParameter *sp);
private:
};
CLocalClass *CP;


void __attribute__ ((constructor)) LibAttach(void)
{
	CP = new CLocalClass;
}


void __attribute__ ((destructor)) LibDetach(void) {
	delete CP;
}


int LLibSum(int nInA, int nInB)
{
	int nRet = (CP->Sum)(nInA, nInB);
	return(nRet);
}


int LLibCalcX10(double *dpOut, double *dpIn, int nInLen)
{
	int nRet = (CP->CalcX10)(dpOut, dpIn, nInLen);
	return(nRet);
}


int LLibTxRxMessage(char *bpOut, char *bpIn, int nInLen)
{
	int nRet = (CP->TxRxMessage)(bpOut, bpIn, nInLen);
	return(nRet);
}


int LLibInOutStr(tagLibParameter *sp)
{
	int nRet = (CP->InOutStr)(sp);
	return(nRet);
}


//================================================================================
// CLocalClass
//================================================================================
CLocalClass::CLocalClass(void)
{
	printf("start DLL\n");
}


CLocalClass::~CLocalClass(void)
{
	printf("end DLL\n");
}


int CLocalClass::Sum(int nInA, int nInB)
{
	int nSum = nInA + nInB;
	return(nSum);
}


int CLocalClass::CalcX10(double *dpOut, double *dpIn, int nInLen)
{
	for(int nLoop=0; nLoop<nInLen; nLoop++) {
		(dpOut[nLoop]) = (dpIn[nLoop]) * 10.0;
	}
	return(0);
}


int CLocalClass::TxRxMessage(char *bpOut, char *bpIn, int nInLen)
{
	char sMsg[256];
	int nMsgLen, nCpLen;
	//--------
	sprintf(sMsg, "DLL->App: Hi ! rcvd[%s]", bpIn);
	nMsgLen = (int)strlen(sMsg);
	nCpLen = nMsgLen;
	if(nInLen<=nCpLen) {
		nCpLen = nInLen - 1;
	}
	memcpy(bpOut, sMsg, nCpLen);
	(bpOut[nCpLen]) = 0;
	return(nCpLen);
}


int CLocalClass::InOutStr(tagLibParameter *sp)
{
	struct timespec ts;
	struct tm *pTm;

	(sp->dOut) = (sp->dInA) + (sp->dInB);

	int nRet = timespec_get(&ts, TIME_UTC);
	pTm = localtime(&(ts.tv_sec));
	sprintf((sp->sData), "%04d %02d/%02d %02d:%02d:%02d %03d",
		((pTm->tm_year)+1900), ((pTm->tm_mon)+1), (pTm->tm_mday),
		(pTm->tm_hour), (pTm->tm_min), (pTm->tm_sec), (int)((ts.tv_nsec)/(1000*1000)));

	return(0);
}

ヘッダーファイルは上述の通りエクスポート関数をextern "C" { }で囲みます。

LocalLib.h
typedef struct {
	double dInA;
	double dInB;
	double dOut;
	char sData[64];
} tagLibParameter;

extern "C" {
int LLibSum(int nInA, int nInB);
int LLibCalcX10(double *dpOut, double *dpIn, int nInLen);
int LLibTxRxMessage(char *bpOut, char *bpIn, int nInLen);
int LLibInOutStr(tagLibParameter *sp);
}

次のようにビルドして下さい。

g++ -shared -fPIC -o libLocalLib.so LocalLib.cpp

呼び出し側のサンプルコード

ライブラリーの動作確認のため、とりあえずここでは呼び出し側のサンプルコードをCで書きました。
配列と文字列のテストでは、ライブラリーへ渡す配列(文字列)と共にライブラリーから処理結果を受け取る配列(文字列)も呼び出し側で用意して2つの配列(文字列)のアドレスをライブラリーへ伝えます。

2025_1120 追記  コードに誤りがあり修正しました。ご指摘ありがとうございました。

main.c
#include <stdio.h>
#include <string.h>

typedef struct {
	double dInA;
	double dInB;
	double dOut;
	char sData[64];
} tagLibParameter;

extern int LLibSum(int nInA, int nInB);
extern int LLibCalcX10(double *dpOut, double *dpIn, int nInLen);
extern int LLibTxRxMessage(char *bpOut, char *bpIn, int nInLen);
extern int LLibInOutStr(tagLibParameter *sp);

int main()
{
	int nA, nB, nSum;
	nA = 1;
	nB = 2;
	//---------------------
	nSum = LLibSum(nA, nB);
	//---------------------
	printf("%d + %d = %d\n", nA, nB, nSum);
	//================================================================
	double dsOut[3], dsIn[3];
	int nLen = (sizeof(dsOut)) / (sizeof(double));
	dsIn[0] = 1.1;
	dsIn[1] = 2.2;
	dsIn[2] = 3.3;
	//----------------------------------------------------------
	LLibCalcX10(dsOut, dsIn, ((sizeof(dsIn))/(sizeof(double))));
	//----------------------------------------------------------
	printf("in[%.1lf %.1lf %.1lf]  out[%.1lf %.1lf %.1lf]\n",
			 (dsIn[0]),  (dsIn[1]),  (dsIn[2]),
			(dsOut[0]), (dsOut[1]), (dsOut[2]) );
	//================================================================
	char sTxBuf[64], sRxBuf[256];
	sprintf(sTxBuf, "App->DLL: Hello !");
	//----------------------------------------------
	LLibTxRxMessage(sRxBuf, sTxBuf, sizeof(sRxBuf));
	//----------------------------------------------
	printf("%s\n", sRxBuf);
	//================================================================
	tagLibParameter LP;
	memset(&LP, 0, sizeof(tagLibParameter));
	(LP.dInA) = 1.1;
	(LP.dInB) = 2.2;
	//----------------
	LLibInOutStr(&LP);
	//----------------
	printf("%.1lf + %.1lf = %.1lf  [%s]\n",
			(LP.dInA), (LP.dInB), (LP.dOut), (LP.sData));
}

Linuxではライブラリーを /usr/local/lib などへコピーして利用することが一般的ですが、ここでは呼び出し側の実行ファイルと同じ場所にあるライブラリーを利用する例を紹介します。
次のようにビルドして下さい。

gcc -o main.o -c main.c
gcc -L./ -Wl,-rpath=./ -o main main.o -lLocalLib

ライブラリーの名前が libLocalLib.so と変てこりんなのは、Linuxのライブラリー名はlibから始まるのが一般的で、リンク時は頭のlibを取ったライブラリー名を使用するからです。

実行するとこのようになります。呼び出し側とライブラリー間で、変数・配列・文字列・構造体の双方向のやりとりが出来ています。

$ ./main
start DLL
1 + 2 = 3
in[1.1 2.2 3.3]  out[11.0 22.0 33.0]
DLL->App: Hi ! rcvd[App->DLL: Hello !]
1.1 + 2.2 = 3.3  [2025 11/18 19:43:02 398]
end DLL
0
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?