
DynLibは不可欠な開発ツールになりました。 猫の下で結果を共有します。
従来のDLL実装アプローチの欠点
従来のアプローチ(実装)の主な欠点は次のとおりです。- 名前空間を使用する能力の欠如
- 多数のユーティリティコードが必要です。
- ライブラリの動的ロードを実装する場合;
- 記述子(または他の暗黙的な構造)およびラッパークラスを使用して、クラスを通じてモジュール間相互作用を実装する場合。
- エクスポートされた関数が例外をスローする可能性がある場合、エラーリターンメカニズムを実装するとき。
DynLibの例
1.通常のDLLを使用する
チャレンジ。 ヘッダーファイルに表示されるインターフェイスを使用して、簡単な数学演算を実装するtest_lib.dllライブラリを動的に接続して使用します。
//========== test_lib.h ========== #pragma once extern "C" __declspec(dllexport) int __stdcall sum(int x, int y); extern "C" __declspec(dllexport) int __stdcall mul(int x, int y); extern "C" __declspec(dllexport) double __stdcall epsilon();
解決策。 次のヘッダーファイルを作成し、プロジェクトに接続する必要があります。
//========== test_lib.hpp ========== #pragma once #include <dl/include.hpp> DL_NS_BLOCK(( test ) ( DL_C_LIBRARY( lib ) ( ( int, __stdcall, (sum), (int,x)(int,y) ) ( int, __stdcall, (mul), (int,x)(int,y) ) ( double,__stdcall, (epsilon), () ) ) ))
プリプロセッサは、DLLを動的にロードし、リストされたsum 、 mul、およびepsilon関数を含むtest :: libクラスを生成します。 DLLをアプリケーションに接続するには、提供されたヘッダーファイルtest_lib.hppをソースコードに含める必要があります。 次に、クラスtest :: libのオブジェクトを作成します。 エクスポートされたDLL関数へのアクセスは、「。」を通じて可能です または「->」。
//========== exe.cpp ========== #include "test_lib.hpp" int main() { test::lib lib( "path_to/test_lib.dll" ); int s = lib->sum( 5, 20 ); int m = lib.mul( 5, 10 ); double eps = lib.epsilon(); return 0; }
2. calculator.dllライブラリーの作成
チャレンジ。 ライブラリcalculator.dllを作成します。これは、合計、2つの数値の積、平方根値を計算する必要があります。 ライブラリを動的にロードし、各関数を呼び出します。
解決策
//========== calculator.hpp ========== #include <dl\include.hpp> DL_NS_BLOCK(( team ) ( DL_LIBRARY( calculator ) ( ( double, sum, (double,x)(double,y) ) ( double, mul, (double,x)(double,y) ) ( double, sqrt, (double,x) ) ) )) //========== calculator_dll.cpp ========== #include "calculator.hpp" struct calculator { static double sum( double x, double y ) { return x + y; } static double mul( double x, double y ) { return x * y; } static double sqrt( double x ) { return std::sqrt(x); } }; DL_EXPORT( team::calculator, calculator )
DLLを使用する
//========== application.cpp ========== #include <iostream> #include "calculator.hpp" int main() { using namespace std; team::calculator calc( "calculator.dll" ); cout << "sum = " << calc.sum(10, 20) << endl; cout << "mul = " << calc.mul(10, 20) << endl; cout << "sqrt = " << calc.sqrt(25) << endl; return 0; }
3. calculator.dllライブラリーのアップグレード。 例外の使用。
チャレンジ。 入力値が正しくない場合、calculator.dllライブラリの平方根sqrt関数はエラーを返します。
解決策
//========== calculator.hpp ========== #include <dl\include.hpp> DL_NS_BLOCK(( team ) ( DL_LIBRARY( calculator ) ( ( double, sqrt, (double,x) ) ) )) //========== calculator_dll.cpp ========== #include "calculator.hpp" struct calculator { static double sqrt( double x ) { if ( x < 0 ) throw std::invalid_argument( " 0" ); return std::sqrt( x ); } }; DL_EXPORT( team::calculator, calculator )
DLLを使用する
//========== application.cpp ========== #include <iostream> #include <locale> #include "calculator.hpp" int main() { using namespace std; locale::global( locale("", locale::ctype) ); try { team::calculator calc( "calculator.dll" ); cout << "sqrt1 = " << calc.sqrt( 25 ) << endl; cout << "sqrt2 = " << calc.sqrt( -1 ) << endl; } catch (dl::method_error const& e) { cerr << "what: " << e.what() << endl; } return 0; } //========== ==========
sqrt1 = 5 what: exception 'class std::invalid_argument' in method 'sqrt' of class '::team::calculator' with message ' 0'
4. shapes.dllライブラリの実装。 インターフェイスの使用。
チャレンジ。 幾何学的図形(正方形、長方形、円)を操作するためのshapes.dllライブラリを作成します。 すべての図は、図の中心の座標を見つけることができる共通のインターフェースをサポートする必要があります。
解決策
//========== shapes.hpp ========== #include <dl/include.hpp> DL_NS_BLOCK(( shapes ) ( DL_INTERFACE(figure) ( ( char const*, name, ) ( double, center_x, ) ( double, center_y, ) ( void, center_xy, (double&,x)(double&,y) ) ) )) DL_NS_BLOCK(( shapes ) ( DL_LIBRARY(lib) ( ( shapes::figure, create_rectangle, (double,left)(double,top)(double,width)(double,height) ) ( shapes::figure, create_square, (double,left)(double,top)(double,size) ) ( shapes::figure, create_circle, (double,center_x)(double,center_y)(double,radius) ) ) )) //========== shapes_lib.cpp ========== #include "shapes.hpp" class rectangle { public: rectangle(double l, double t, double w, double h) : l_(l), t_(t), w_(w), h_(h) { if (w < 0) throw std::invalid_argument( " " ); if (h < 0) throw std::invalid_argument( " " ); } char const* name() { return "rectangle"; } double center_x() { return l_ + w_ / 2.; } double center_y() { return t_ + h_ / 2.; } void center_xy(double& x, double& y) { x = center_x(); y = center_y(); } private: double l_, t_, w_, h_; }; class square { public: square(double l, double t, double s) : l_(l), t_(t), s_(s) { if (s < 0) throw std::invalid_argument( " " ); } char const* name() { return "square"; } double center_x() { return l_ + s_ / 2.; } double center_y() { return t_ + s_ / 2.; } void center_xy(double& x, double& y) { x = center_x(); y = center_y(); } private: double l_, t_, s_; }; class circle { public: circle(double x, double y, double r) : x_(x), y_(y), r_(r) { if (r < 0) throw std::invalid_argument( " " ); } char const* name() { return "circle"; } double center_x() { return x_; } double center_y() { return y_; } void center_xy(double& x, double& y) { x = x_; y = y_; } private: double x_, y_, r_; }; struct shapes_lib { static shapes::figure create_rectangle( double l, double t, double w, double h ) { return dl::shared<rectangle>( l, t, w, h ); } static shapes::figure create_square( double l, double t, double s ) { return dl::shared<square>( l, t, s ); } static shapes::figure create_circle( double x, double y, double r ) { return dl::shared<circle>( x, y, r ); } }; DL_EXPORT( shapes::lib, shapes_lib ) //========== application.cpp ========== #include <iostream> #include "shapes_lib.hpp" void print_center( shapes::figure shape ) { std::cout << shape.name() << ": " << shape.center_x() << "-" << shape.center_y() << std::endl; } int main() { shapes::lib lib( "shapes.dll" ); print_center( lib.create_circle(10, 10, 10) ); print_center( lib.create_square(0, 0, 20) ); print_center( lib.create_rectangle(0, 5, 20, 10) ); return 0; }
ライブラリの接続方法
ライブラリはヘッダーファイルの形式で提供されます。 .libと.dllは必要ありません。 接続するには、次のディレクティブを追加します。
#include <dl/include.hpp>
ライブラリアイテム
DynLibライブラリの多くのクラスとマクロは、互いに独立して別々に使用できます。
DL_BLOCK
他のすべてのマクロのコンテナとして機能します。
DL_BLOCK ( // declarations )
DL_NS_BLOCK
他のすべてのマクロのコンテナとして機能します。 クラスの名前空間を作成します。
DL_NS_BLOCK( (ns0, ns1, ns2 … )/* , 10*/ ( // declarations ))
DL_EXPORTを除く以下で説明するマクロは、 DL_BLOCKまたはDL_NS_BLOCKに配置する必要があります
DL_C_LIBRARY
マクロの目的は、DLLの動的なロードと関数の自動インポートを実装する既製のクラスをユーザーに提供することです。 マクロは次のように表示されます。 DL_C_LIBRARY(lib_class) ( /*functions*/ ( ret_type, call, (name, import_name), arguments ) )
- lib_class-実装がDynLibライブラリによって生成されるクラスの名前。
- functions -DLLによってエクスポートされた関数のリスト。 次の形式のリストを介して設定
(ret_type、call、(name、import_name)、引数)- ret_type-関数によって返される値のタイプ。
- call- 呼び出し形式。例:__sdtcall、__ cdeclなど。
- name-関数名(ユーザー用);
- import_nameは、DLLエクスポートテーブルで指定された関数の名前で、装飾(存在する場合)を含みます。 nameとimport_nameが一致する場合、import_nameは省略できます。
- arguments-入力引数を指定するリスト(引数タイプ、引数名、デフォルト値)。 引数名とデフォルト値は省略できます。
DL_BLOCK ( DL_C_LIBRARY( my_lib ) ( ( void, __stdcall, (func), (int)(int,s)(double,V,=1.0) ) ( int, __stdcall, (fn, "fn@0"), (int,a) ) ( int, __stdcall, (fn), () ) ) )
DL_RECORD
DL_RECORDマクロは、 モジュール間通信で使用するためのパックデータ構造を生成します。 さらに、マクロにリストされているすべての引数を使用してコンストラクターが作成されます。
DL_RECORD( record_name ) ( /*fields*/ (type, name, =default_value) )
例:
//========== some_exe.cpp ========== #include <dl/include.hpp> DL_BLOCK ( DL_RECORD( data ) ( ( int, x ) ( int, y, = 100 /* */ ) ( int, z, = 200 /* */ ) ) ) int main() { data v( 20 ); // x = 20, y = 100, z = 200 vx = 10; vy = vx; vz = 50; v = data( 5, 20, 30 ); data a( 1, 2, 3 ); return 0; }
DL_LIBRARY
DL_LIBRARYマクロは、いくつかのタスクを実行します。
- EXE(DLL)とDLL間のインターフェースの説明(ドキュメント)として機能します。
- ライブラリ関数を開発者に自動的にエクスポートするために必要な構造が含まれています。
- 指定されたインターフェイスでDLLをロードし、ユーザーがエクスポートされた関数にアクセスできるクラスを実装します。
- C ++例外の正しい使用を提供します。
-DLL側のC ++例外の自動キャッチ。 -DLLの境界を介して値を返し、例外の存在を通知します。 -例外がDLLの側でキャッチされた場合の新しい例外の生成(例外の種類に関する説明と情報の復元)
DL_LIBRARY( name ) ( /*functions*/ ( ret_type, name, arguments ) )
DL_LIBRARYマクロによって生成されたクラスは、DLLの境界を越えて渡すことはできません。
マクロがどのように機能するかを示すために、次のヘッダーファイルを想像してください。
//========== test1_lib.hpp ========== #pragma once #include <dl/include.hpp> DL_NS_BLOCK(( team, test ) ( DL_LIBRARY( lib ) ( ( int, sum, (int,x)(int,y) ) ( void, mul, (int,x)(int,y)(int&,result) ) ( double, epsilon, () ) ) ))
この説明は、DLL開発者がDL_EXPORTマクロを使用して関数をエクスポートするために使用します。 test1_lib.hppヘッダーファイルを接続することにより、ユーザーはDLLの操作をすぐに開始できます。
//========== test1_exe.cpp ========== #include <test1_lib.hpp> int main() { team::test::lib lib( "test1.dll" ); int s = lib.sum( 5, 10 ); lib.mul( 5, 5, s ); double eps = lib->epsilon(); return 0; }
DL_EXPORT
DL_EXPORTマクロは、 DLL関数をエクスポートするように設計されています。
DL_EXPORT ( lib_class 、 lib_impl_class )
- lib_class-対話インターフェースを記述するクラスのフルネーム(DL_LIBRARYで使用されたクラス名);
- lib_impl_classは、対話インターフェースで指定された機能を実装するクラスの完全修飾クラス名です。
- クラス(構造)を作成します。
- インターフェイスからの各関数を静的として定義します。 関数のスコープはpublic :;でなければなりません。
- DL_EXPORT(lib、impl)コンストラクトを記述して、関数をエクスポートします。
//========== test1_dll.cpp ========== #include "test1_lib.hpp" struct lib_impl { static int sum( int x, int y ) { return x + y; } static void mul( int x, int y, int& result ) { result = x + y; } static double epsilon() { return 2.0e-8; } }; DL_EXPORT( team::test::lib, lib_impl )
DL_INTERFACE
このマクロを使用すると、クラスインターフェイスを記述し、それを操作するラッパークラスをユーザーに提供できます。 ラッパークラスの実装により、C ++例外の正しい使用が保証されます。
-DLL側のC ++例外の自動キャッチ。 -DLLの境界を介して値を返し、例外の存在を通知します。 -例外がDLLの側でキャッチされた場合の新しい例外の生成(例外の種類に関する説明と情報の復元)このマクロによって生成されたラッパークラスは、このインターフェイスを実装するオブジェクトの所有権を共有しています。 共有所有権は、リンクカウントメカニズムによって提供されます。 ラッパークラスのオブジェクトをコピーする場合、参照カウンターを増やすために内部関数が呼び出され、破棄される場合、参照カウンターを減らすために内部関数が呼び出されます。 カウンターが0に達すると、オブジェクトは自動的に削除されます。 インターフェイスメソッドへのアクセスは「。」を介して または「->」。
DynLibライブラリは、EXE境界(DLL)<-> DLLでのインターフェイスクラスの安全な使用を保証します
DL_INTERFACE( interface_class ) ( /*methods*/ ( ret_type, name, arguments ) )
- interface_class -DynLibライブラリが生成する実装を持つクラスの名前。
- メソッド -クラスインターフェイスを記述する関数の列挙、
DL_NS_BLOCK(( example ) ( DL_INTERFACE( processor ) ( ( int, threads_count, () ) ( void, process, (char const*,buf)(std::size_t,size) ) ) ))
使用法:
example::processor p; p =… // . dl::shared dl::ref int tcount = p->threads_count(); p.process(some_buf, some_buf_size);
dl ::共有
テンプレートクラスdl :: shared <T>は、次の問題を解決します。
- コンストラクターに渡された引数を持つクラスTのオブジェクトの動的作成。
- 参照カウンターを追加して共有所有権を提供する(boost(std):: shared_ptrなど);
- DL_INTERFACEマクロによって生成されたクラスオブジェクトへの暗黙的なキャスト。
dl ::共有クラスは、DLLの境界を越えて渡すことはできません 。
クラスmy_processorと例::プロセッサインターフェイスがあるとします:
class my_processor { public: my_processor( char const* name = "default name" ); int threads_count(); void process(char const* buf, std::size_t size); private: // }; DL_NS_BLOCK(( example ) ( DL_INTERFACE( processor ) ( ( int, threads_count, () ) ( void, process, (char const*,buf)(std::size_t,size) ) ) ))
dl :: sharedの使用例を以下に示します。
dl::shared<my_processor> p1( "some processor name" ); // my_processor dl::shared<my_processor> p2; // my_processor c dl::shared<my_processor> p3( p1 ); // p3 p1 , = 2 dl::shared<my_processor> p4( dl::null_ptr ); // p4 p3.swap( p4 ); // p4 , p1, p3 — p4 = dl::null_ptr; // p4 p2 = p1; // p2 p1 p2 = p1.clone(); // my_processor // my_processor p2->threads_count(); p2->process( /*args*/ ); // my_processor example::processor pi = p2; // my_processor example::processor // pi , , . pi->threads_count(); pi->process(/*args*/); // my_processor pi.
dl :: ref
同じメソッドのセットを使用して、 DL_INTERFACEを介して宣言されたインターフェイスクラスのオブジェクトに任意のオブジェクトをキャストできるライブラリ関数。 通常、この動作は、インターフェイスクラスを引数として受け取る関数がある場合に必要であり、スタックに配置されるオブジェクトを渡す必要があります。
dl :: ref関数は注意して使用する必要があります。この場合、インターフェースクラスのオブジェクトは転送されたオブジェクトを所有せず、ユーザーはインターフェースクラスを通じてオブジェクトの寿命とその使用を制御します。 dl :: refを介して渡されたオブジェクトを参照するインターフェースクラスのオブジェクトをコピーすることは許可されており、非常に正確です(参照カウンターがないため、変更するものはありません-インターフェースクラスのオブジェクトはここで正しく動作する方法を知っています)。
class my_processor { public: my_processor( char const* name = "default name" ); int threads_count(); void process( char const* buf, std::size_t size ); private: // }; DL_NS_BLOCK(( example ) ( DL_INTERFACE( processor ) ( ( int, threads_count, () ) ( void, process, (char const*,buf)(std::size_t,size) ) ) )) void some_dll_func( example::processor p ) { // p } int main() { my_processor processor( "abc" ); some_dll_func( dl::ref(processor) ); // , dl::object<my_processor> return 0; }
サポートされているコンパイラ
DynLibライブラリは 、次のコンパイラ(開発環境) と完全に互換性があります。
- Microsoft Visual C ++ 2008;
- Microsoft Visual C ++ 2010;
- MinGW GCC 4.5.0以降。
- CodeGear C ++ Builder XE(特定のコンパイラー設定では動作は保証されません)