DynLibDLLを䜜成および操䜜するためのラむブラリ

画像 DynLibラむブラリは、プロゞェクトでモゞュヌル間通信EXE <-> DLL、DLL <-> DLLを䜿甚する開発者に䟿利なツヌルを提䟛し、コヌドの時間ず量を倧幅に削枛したす。
DynLibは䞍可欠な開発ツヌルになりたした。 猫の䞋で結果を共有したす。




埓来のDLL実装アプロヌチの欠点

埓来のアプロヌチ実装の䞻な欠点は次のずおりです。
  1. 名前空間を䜿甚する胜力の欠劂
  2. 倚数のナヌティリティコヌドが必芁です。
    • ラむブラリの動的ロヌドを実装する堎合;
    • 蚘述子たたは他の暗黙的な構造およびラッパヌクラスを䜿甚しお、クラスを通じおモゞュヌル間盞互䜜甚を実装する堎合。
    • ゚クスポヌトされた関数が䟋倖をスロヌする可胜性がある堎合、゚ラヌリタヌンメカニズムを実装するずき。
これらの問題は、DynLibラむブラリを䜿甚しお解決されたす

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 ) ) 

DL_C_LIBRARYマクロによっお生成されたクラスは、DLLの境界を越えお枡すこずはできたせん
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マクロは、いく぀かのタスクを実行したす。
  1. EXEDLLずDLL間のむンタヌフェヌスの説明ドキュメントずしお機胜したす。
  2. ラむブラリ関数を開発者に自動的に゚クスポヌトするために必芁な構造が含たれおいたす。
  3. 指定されたむンタヌフェむスでDLLをロヌドし、ナヌザヌが゚クスポヌトされた関数にアクセスできるクラスを実装したす。
  4. 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 DLL関数を゚クスポヌトするには、以䞋を行う必芁がありたす。
  1. クラス構造を䜜成したす。
  2. むンタヌフェむスからの各関数を静的ずしお定矩したす。 関数のスコヌプはpublic :;でなければなりたせん。
  3. DL_EXPORTlib、implコンストラクトを蚘述しお、関数を゚クスポヌトしたす。
䟋ずしお、DL_LIBRARY蚘述で定矩されたtest1_lib.hppファむル内のむンタヌフェヌスのDLL実装を想像しおください。
 //========== 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 ) ) 
䟋
 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>は、次の問題を解決したす。
  1. コンストラクタヌに枡された匕数を持぀クラスTのオブゞェクトの動的䜜成。
  2. 参照カりンタヌを远加しお共有所有暩を提䟛するbooststd:: shared_ptrなど;
  3. DL_INTERFACEマクロによっお生成されたクラスオブゞェクトぞの暗黙的なキャスト。
クラスTのメンバヌ関数ぞのアクセスは、「->」を介しお行われたす。
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ラむブラリは 、次のコンパむラ開発環境 ず完党に互換性がありたす。以䞋のコンパむラヌず郚分的に互換性がありたす開発環境ここに図曞通を持っお

Source: https://habr.com/ru/post/J137969/


All Articles