DynLib: DLL के साथ निर्माण और काम करने के लिए पुस्तकालय

छवि DynLib पुस्तकालय अपनी परियोजनाओं में अंतर-मॉड्यूल संचार (EXE <-> DLL, DLL <-> DLL) का उपयोग कर डेवलपर्स के लिए सुविधाजनक उपकरण प्रदान करता है और कोड के समय और मात्रा को काफी कम कर देता है।
डायनलिब एक अभिन्न विकास उपकरण बन गया है। बिल्ली के नीचे हम परिणाम साझा करते हैं।




एक पारंपरिक 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 को लोड करता है और इसमें सम , mul और एप्सिलॉन फ़ंक्शन सूचीबद्ध होते हैं। DLL को एप्लिकेशन से कनेक्ट करने के लिए, आपको दिए गए हेडर फ़ाइल test_lib.hpp को स्रोत कोड में शामिल करना होगा। अगला, वर्ग परीक्षण की एक वस्तु बनाएँ :: 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 लाइब्रेरी बनाना


टास्क। एक लाइब्रेरी कैलकुलेटर लिखें ।ll, जो योग, दो नंबरों के उत्पाद और वर्गमूल मूल्य की गणना करना चाहिए। लायब्रेरी को गतिशील रूप से लोड करें और प्रत्येक फ़ंक्शन को कॉल करें।
निर्णय
 //========== 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 लाइब्रेरी को अपग्रेड करना। अपवादों का उपयोग।


टास्क। कैलकुलेटर में सभी वर्गाकार रूट 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. आकृतियों का कार्यान्वयन। पुस्तकालय। इंटरफेस का उपयोग।


टास्क। ज्यामितीय आकृतियों (वर्ग, आयत, वृत्त) के साथ काम करने के लिए एक shape.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 की तरफ से पकड़ा गया था (अपवाद के प्रकार के बारे में विवरण और जानकारी की बहाली के साथ)।
    	
 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, () ) ) )) 

इस विवरण का उपयोग DL_ डेवलपर द्वारा 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. इंटरफ़ेस से प्रत्येक फ़ंक्शन को स्थिर के रूप में परिभाषित करें। कार्यक्षेत्र सार्वजनिक होना चाहिए :;
  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 की तरफ से पकड़ा गया था (अपवाद के प्रकार के बारे में विवरण और जानकारी की बहाली के साथ)।
	
इस मैक्रो द्वारा उत्पन्न रैपर क्लास ने एक ऑब्जेक्ट का स्वामित्व साझा किया है जो इस इंटरफ़ेस को लागू करता है। साझा स्वामित्व एक लिंक गिनती तंत्र द्वारा प्रदान किया जाता है, अर्थात। आवरण वर्ग की वस्तुओं की प्रतिलिपि बनाते समय, संदर्भ काउंटर को बढ़ाने के लिए एक आंतरिक फ़ंक्शन को कहा जाता है; जब नष्ट हो जाता है, तो संदर्भ काउंटर को कम करने के लिए एक आंतरिक फ़ंक्शन कहा जाता है। जब काउंटर 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 :: साझा <T> निम्नलिखित समस्याओं को हल करता है:
  1. निर्माणकर्ता में पारित तर्कों के साथ कक्षा टी की एक वस्तु का गतिशील निर्माण;
  2. एक संदर्भ काउंटर जोड़ना और साझा स्वामित्व प्रदान करना (जैसे बूस्ट (std) :: share_ptr);
  3. DL_INTERFACE मैक्रो द्वारा उत्पन्न वर्ग ऑब्जेक्ट को अंतर्निहित कास्टिंग।
कक्षा T के सदस्य कार्यों तक पहुँच '->' के माध्यम से है।
डीएल :: साझा कक्षाएं डीएलएल सीमाओं के पार नहीं जा सकतीं
मान लीजिए कि एक वर्ग 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 :: साझा का उपयोग करने के उदाहरण नीचे प्रस्तुत किए गए हैं:
 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 :: रेफ

लाइब्रेरी फ़ंक्शन जो आपको 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/In137969/


All Articles