[オープンソースワールド]ダイナミックリンクライブラリインタフェースの自動エクスポートから見たC++の欠点


ダイナミックリンクライブラリインタフェースを自動的にエクスポートすることは、C++プログラミングでは絶対に煩わしいことです.以下の手順を大量に繰り返さなければなりません.1.ダイナミックリンクライブラリ2をロードする.書き出し関数ポインタ定義3を定義する.書き出し関数ポインタ変数4を定義する.動的リンクライブラリから関数5をエクスポートする.導出関数のパッケージ関数6を記述する.ダイナミックリンクライブラリをアンインストールすると、このプロセスを自動化しようとする人がたくさんいますが、すべてが砂に沈んでいます.究極の理由は,動的リンクライブラリが関数をエクスポートする際にパラメータ情報を含まず,使用する際にエクスポート関数を強制的に変換せざるを得ないためであると考えられる.C#でダイナミックリンクライブラリをエクスポートする関数は、C++よりも簡単です.1.DllImportAttributeプロパティを使用して、エクスポート関数のファイルパス、エクスポートポイント、スタック呼び出し方式などを規定する.エクスポート関数を定義するC#関数のプロトタイプは、単純はコード単純であり、実装プロセスが単純ではないと言います.表面的に見ると、C#のこの2部はC++の6ステップを実現し、原理的に見ると、C#は依然としてC++の6ステップを必要としている.C#のコンパイラは2,3,4,5の手順をカプセル化し,C#コードを中間言語にコンパイルする際に展開した.C#導出関数の関数プロトタイプを定義する際に必要なことは、C#パラメータタイプとC++パラメータタイプの対応関係を判断することですが、なぜ特に注意するのでしょうか.C#は展開時に正確なC++関数定義が必要であるため、呼び出し中にエラーが発生します.Javaには、ローカルダイナミックリンクライブラリを呼び出すことができるコンポーネントであるJNativeがあります.具体的にどのように実装されているかは見たことがありませんが、JNativeはC#の実装プロセスを参照し、JNativeが実装時にJavaパラメータタイプとC++パラメータタイプの対応関係を判断する必要があるため、JNativeは実装時にJavaパラメータタイプとC++パラメータタイプの対応関係を判断する必要があると推測しています.C#の実装方式とJavaの実装方式は本質的にC++と区別されないが、符号化の過程ではC#とJavaのコンパイラが重複コードの処理でC++より多くの仕事をしたため、簡単である.C++はもちろん重複コードも処理できますが、コンパイラではなくマクロで処理されます.私はC#の考え方に従ってC++自動導出関数の簡単なバージョンを実作しました.
#pragma once

#include <WTypes.h>

#include <list>
using namespace std;

class DllImportAttribute
{
public:
	//        
	virtual BOOL Init() = 0;

	//        
	virtual BOOL Uninit() = 0;
};

//      
BOOL DllImportInit();

//      
BOOL DllImportUninit();

//      
extern list<DllImportAttribute*> gDllImportList;

#define DLLIMPORTCLASSBEGIN(CLASS, DLLPATH)	class CLASS;	\
extern CLASS g##CLASS##Dll;	\
class CLASS : public DllImportAttribute	\
{	\
protected:	\
	HMODULE  m_hModule;	\
public:	\
	CLASS() : m_hModule(NULL) {	\
		gDllImportList.push_back(this);	\
	}	\
	virtual BOOL Init() {	\
		m_hModule = LoadLibraryA(DLLPATH);	\
		return (m_hModule != NULL);	\
	}	\
	virtual BOOL Uninit() {	\
		return FreeLibrary(m_hModule);	\
	}	\

#define FUNCTIONENTRY(ENTRYTYPE, ENTRYPOINT)	protected:	\
	typedef ENTRYTYPE;	\
	ENTRYPOINT ENTRYPOINT##Ptr;	\
public:	\
	ENTRYPOINT ENTRYPOINT##Func() {	\
		ENTRYPOINT##Ptr = (ENTRYPOINT)GetProcAddress(m_hModule, "##ENTRYPOINT##");	\
		return ENTRYPOINT##Ptr;	\
	}	\

#define DLLIMPORTCLASSEND() };

#define DLLIMPORTCLASSIMPLEMENT(CLASS)	CLASS g##CLASS##Dll;

#define DLLIMPORTCALL(CLASS, ENTRYPOINT)	g##CLASS##Dll.ENTRYPOINT##Func()
#include "StdAfx.h"
#include "DllImportAttribute.h"

list<DllImportAttribute*> gDllImportList;

//      
BOOL DllImportInit()
{
	BOOL bSuccess = TRUE;
	for (auto iter = gDllImportList.begin(); iter != gDllImportList.end(); iter++)
	{
		bSuccess &= (*iter)->Init();
	}
	return bSuccess;
}

//      
BOOL DllImportUninit()
{
	BOOL bSuccess = TRUE;
	for (auto iter = gDllImportList.begin(); iter != gDllImportList.end(); iter++)
	{
		bSuccess &= (*iter)->Uninit();
	}
	return bSuccess;
}

上の2つのファイルはDLL自動導出関数を実現する基礎フレームワークであり,直接プロジェクトに参照される.
上記のフレームワークを使用して、DLL関数の導出プロセスを実現します(
MessageBoxA
例として
)は次のとおりです.
#pragma once

#include "DllImportAttribute.h"

DLLIMPORTCLASSBEGIN(User32, "User32.dll")
	FUNCTIONENTRY(int (WINAPI *MessageBoxA) (HWND, LPCSTR, LPCSTR, UINT), MessageBoxA)
DLLIMPORTCLASSEND()
#include "stdafx.h"
#include "User32.h"

DLLIMPORTCLASSIMPLEMENT(User32)

簡単になったように見えます.クライアントがエクスポート関数を使用する例:
DllImportInit();
DLLIMPORTCALL(User32, MessageBoxA)(NULL, "ad", "ad", MB_OK);
DllImportUninit();

フレームワークの実現過程を見渡すと
DLIMPORTCLASSBEGIN、FUNCTIOONNTRY、DLIMPORTCALLの3つのマクロの意味は大きい:
DLIMPORTCLASSBEGINはダイナミックリンクライブラリのロードとアンインストールを実現した.
FUNCTIOONENTRYは導出関数の定義とパッケージ関数を実現した.
DLIMPORTCALLは、エクスポート関数の呼び出しを簡単にします.
マクロによってクライアント符号化が比較的簡単になるとはいえ、C#の単純さには至っていない.C#のシンプルさを達成するには、少なくとも以下の点が必要です.
1.C++では、変数の再定義エラーを起こさずにヘッダファイルに変数を宣言できます.この問題はimportキーワードを導入することで解決しようとする人がいる.
2.C++は、Result、StackCall、Name、Paramsという関数定義の各部分を解析できます.VSバージョンのfunctionテンプレートクラスは,具体的に実装する際にマクロ展開を用い,Result(Params)タイプのパラメータを解析することができ,Result,Paramsパラメータ形式よりも理解できるが,まだ十分ではない.
最新のC++標準C++11は多くの言語特性を増加させ、その中で不定パラメータテンプレートの規則が最も興奮させ、私はまだ具体的な試みがなくて自動あちこちのフレームワークをもっと簡単にすることができますが、上記の2つの問題が徹底的に解決できなければ、C++がC#の簡単さに達することは期待できません.