परिचय
लंबे समय तक मैंने विकास के बारे में लेख नहीं लिखे हैं, हालांकि मुझे वास्तव में लेखन प्रक्रिया पसंद है और मुझे अपने विचारों को क्रम में रखने की अनुमति देता है।
और अब, हाल ही में सामने आए घटनाक्रम के बारे में बात करने का अवसर मिला है। मुझे आशा है कि यह पाठ कुछ लोगों के लिए जीवन को सरल करेगा और नई ऊंचाइयों को जीतने के लिए प्रेरणा देगा।
इस बार हम wxWidgets लाइब्रेरी का उपयोग करके C ++ प्लगइन्स के साथ क्रॉस-प्लेटफ़ॉर्म एप्लिकेशन बनाने पर ध्यान केंद्रित करेंगे। विंडोज, लिनक्स और ओएस एक्स को सबसे लोकप्रिय माना जाएगा।
हमेशा की तरह, पाठकों के लिए प्रवेश सीमा को कम करने के लिए, पहला भाग अवलोकन किया जाएगा। कुछ के लिए, पहले भाग से जानकारी स्पष्ट प्रतीत होगी (विशेष रूप से उपकरण के संबंध में), लेकिन, फिर भी, मैं इसे आवश्यक मानता हूं, क्योंकि शुरुआती लोगों के लिए जानकारी न्यूनतम प्रयास के साथ विकास प्रक्रिया को व्यवस्थित करने की अनुमति देगा।
अगला स्पष्टीकरण के साथ परीक्षण आवेदन कोड का चरणबद्ध विश्लेषण है।
उपकरण
wxWidgets
सबसे पहले हमें चाहिए:
स्रोत कोड में WxWidgets लाइब्रेरी। मैं एसवीएन से नवीनतम संस्करणों का उपयोग करता हूं। बेशक, वे बग के बिना नहीं हैं, लेकिन उन्होंने कार्यक्षमता को लागू किया है जो आमतौर पर आधिकारिक रिलीज में पर्याप्त नहीं है।
स्रोत कोड यहां लिया जा सकता है:
http://svn.wxwidgets.org/svn/wx/wxWidgets/trunkआप यहां पुस्तकालय निर्माण प्रक्रिया के बारे में अधिक पढ़ सकते हैं:
http://habrahabr.ru/post/123588/उपरोक्त लेख की तुलना में निर्माण प्रक्रिया में अंतर केवल इतना है कि आपको
DLL Debug
और
DLL Release
कॉन्फ़िगरेशन का उपयोग
Debug
और
Release
बजाय करने की आवश्यकता है। इसके अलावा, यह जरूरी है कि wxWidgets वितरण में शामिल सभी परियोजनाओं की सेटिंग में,
Multi-Threaded Debug DLL Multi-Threaded DLL
के मान
C/C++ -> Code Generation -> Runtime Library
पैरामीटर में निर्दिष्ट हैं। यह अंत में "DLL" के साथ है। इस मामले में, wxWidgets गतिशील पुस्तकालयों के रूप में और गतिशील CRT के साथ संकलित किया जाएगा।
DLL Debug
और
DLL Release
के कॉन्फ़िगरेशन को इकट्ठा करते समय
DLL Release
हो सकता है कि पहली बार सभी पुस्तकालयों को इकट्ठा नहीं किया जाता है। यह सब निर्भरता को निर्दिष्ट करने की समस्याओं के कारण है। यदि नहीं, तो फिर से विधानसभा शुरू करें। आमतौर पर 2-3 पुनरावृत्तियों गतिशील पुस्तकालयों का एक पूरा सेट प्राप्त करने के लिए पर्याप्त हैं।
आपको याद दिला दूं कि wxWidgets के साथ काम करने के लिए, आपके पास
%WXWIN%
पर्यावरण चर (विंडोज के लिए) होना चाहिए, जो wxWidgets स्रोत फ़ोल्डर को इंगित करता है। लिनक्स और ओएस एक्स के लिए, बस
configure && make && make install
।
कॉन्फ़िगर करने के लिए पैरामीटर:
- डीबग:
configure --enable-shared --disable-static --enable-unicode --disable-compat28 --disable-final --enable-debug
करें configure --enable-shared --disable-static --enable-unicode --disable-compat28 --disable-final --enable-debug
- रिलीज़:
configure --enable-shared --disable-static --enable-unicode --disable-compat28 --enable-final --disable-debug
CMake
विभिन्न सेटिंग्स के साथ विभिन्न कार्य मशीनों पर विभिन्न प्लेटफार्मों के लिए परियोजना फाइलों के निर्माण की सुविधा के लिए, हम CMake परियोजना पीढ़ी प्रणाली का उपयोग करेंगे, जिसके बारे में, वैसे, हैबे पर कुछ अच्छे अवलोकन लेख हैं, उदाहरण के लिए, यहां:
सामान्य तौर पर, CMake एक ऐसा उपकरण है जिसके साथ विभिन्न मशीनों पर हम Visual Studio (Windows), Makefile / CodeBlocks (Linux), Makefile / XCode (OS X) प्रोजेक्ट फाइल्स को सोर्स कोड और थर्ड-पार्टी लाइब्रेरी में सही लिखित रास्तों के साथ बना सकते हैं, जो हमें असेंबली स्थापित करने पर अनावश्यक काम की बड़ी मात्रा से छुटकारा पाने की अनुमति देगा।
आप यहां सीएमके डाउनलोड कर सकते हैं:
http://www.cmake.org/cmake/resources/software.htmlयदि आपने डिबगिंग जानकारी के साथ wxWidgets (Linux, OS X) एकत्र किया है, और फिर रिलीज़ संस्करण इंस्टॉल करना चाहते हैं, तो आपको डीबग संस्करण के लिए स्थापना रद्द करने और मैन्युअल रूप से फ़ाइलों को हटाने की आवश्यकता है
- / usr / स्थानीय / बिन / wx-config
- / usr / स्थानीय / बिन / wxrc
यदि उपरोक्त फ़ाइलों को मैन्युअल रूप से हटाया नहीं जा सकता है, तो लाइब्रेरी के रिलीज़ संस्करण के लिए डीबग संस्करण से सेटिंग्स का उपयोग किया जाएगा। आवेदन इकट्ठा होगा, लेकिन शुरू नहीं होगा।
आपको इस तथ्य को भी ध्यान में रखना होगा कि यदि आपने wxWidgets का डीबग संस्करण स्थापित किया है, तो लिनक्स और ओएस एक्स पर आप सबसे अधिक आवेदन के डीबग संस्करण का निर्माण करने में सक्षम होंगे। वही रिलीज़ संस्करण पर लागू होता है। और सभी क्योंकि सीएमके wx-config स्क्रिप्ट से संकलन और लिंकिंग पैरामीटर लेता है, जो डिफ़ॉल्ट रूप से एक वर्तमान कॉन्फ़िगरेशन के लिए पैरामीटर देता है। या डिबग के लिए अलग से, या रिलीज के लिए अलग से।
दृश्य C ++ (Windows)
हम Windows में स्रोत कोड से wxWidgets और हमारे एप्लिकेशन को बनाने के लिए विजुअल C ++ 2012 का उपयोग करेंगे। एक्सप्रेस संस्करण भी उपयुक्त है। इसका मतलब यह है कि आईडीई और कंपाइलर सहित सभी विकास उपकरण मुफ्त होंगे।
टैंक में उन लोगों के लिए, मुफ्त विजुअल C ++ 2012 का एक लिंक:
http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-productsDialogBlocks
उपयोगकर्ता इंटरफ़ेस बनाने के लिए, इसलिए हाथ से सब कुछ लिखने के लिए नहीं, मैं डायलॉगब्लॉक्स एप्लिकेशन का उपयोग करने की सलाह देता हूं। तो, हाँ, यह भुगतान किया जाता है, लेकिन एक नि: शुल्क परीक्षण संस्करण है, जो सरल रूपों को बनाने के लिए पर्याप्त है। हालांकि, फिर से, कोई भी हाथ से सब कुछ लिखने के लिए परेशान नहीं करता है (वैसे, यह शैक्षिक उद्देश्यों के लिए भी बुरा नहीं है और स्पष्ट रूप से कोड को समझने पर सकारात्मक प्रभाव पड़ता है)।
यहां डायलॉगब्लॉक डाउनलोड करें:
http://www.anthemion.co.uk/dialogblocks/download.htmशुरुआत
फ़ोल्डर संरचना
मैं समझता हूं कि लगा-टिप पेन स्वाद और रंग में भिन्न हैं, और इस पर अपनी कैटलॉग संरचना को लागू करना कृतघ्न है, लेकिन कंपनी में कई वर्षों के काम के बाद हम एक निश्चित संरचना में आए, जो जटिल परियोजनाओं पर अच्छी तरह से काम करती है और जो समझने में काफी सरल है। इसलिए, इस लेख में हम इसका उपयोग करेंगे।
- बिल्ड - प्रोजेक्ट बनाने के लिए एक सामान्य सीएमके स्क्रिप्ट और शेल स्क्रिप्ट के साथ एक फ़ोल्डर
- बिल्ड / बिन / <कॉन्फ़िगरेशन> - वह फ़ोल्डर जहां कंपाइलर बाइनरी फाइलें डालता है
- / शामिल - आम हेडर के साथ फ़ोल्डर (उदाहरण के लिए, precompiled हेडर के लिए)
- / <ProjectName> - मुख्य समाधान से परियोजना स्रोत कोड वाला फ़ोल्डर (समाधान में एक से अधिक परियोजना हो सकती है, प्रत्येक का अपना फ़ोल्डर है)
- / <थर्डपार्टी> - वह फ़ोल्डर जिसमें थर्ड-पार्टी लाइब्रेरी स्थित हैं (स्रोत कोड के रूप में या संकलित, प्रत्येक अपनी उपनिर्देशिका में)
- / थर्डपार्टी / बिल्ड - एक सामान्य सीएमके स्क्रिप्ट के साथ एक फ़ोल्डर और तीसरे पक्ष के पुस्तकालय परियोजनाओं के निर्माण के लिए शेल स्क्रिप्ट (यदि आप उन्हें एक अलग समाधान में डालने का निर्णय लेते हैं)
- / ThirdParty / <LibName> - तृतीय-पक्ष लायब्रेरी के स्रोत कोड वाला फ़ोल्डर (एक से अधिक हो सकते हैं)
- / <ProjectName> / <OS-Name> - यहाँ CMake प्रत्येक OS के लिए प्रोजेक्ट फाइल्स जोड़ता है।
मुख्य CMakeList
मुख्य CMake स्क्रिप्ट में सभी परियोजनाओं के लिए सामान्य पैरामीटर और सेटिंग्स हैं, साथ ही कुछ सामान्य चर का विवरण भी है।
निर्माण / CMakeLists.txt cmake_minimum_required(VERSION 2.6.0)
परियोजनाओं के निर्माण के लिए लिपियों
उपयोग में आसानी के लिए, सीमेक शेल या बैच स्क्रिप्ट का उपयोग करना बेहतर है। यह नियमित संचालन जैसे सीएमके को कॉल करने और पर्यावरण चर स्थापित करने में थोड़ा समय बचाएगा।
विंडोज (cm.bat)
सुविधा के लिए, x86 और x64 के लिए विज़ुअल स्टूडियो प्रोजेक्ट बनाने के लिए अलग बैच स्क्रिप्ट का उपयोग करना बेहतर है, साथ ही एक सामान्य स्क्रिप्ट जो यह निर्धारित करेगी कि हम किस प्लेटफ़ॉर्म के लिए एप्लिकेशन बना रहे हैं:
rem @echo off IF "%1" == "" GOTO NO_PARAMS IF "%1" == "x86" GOTO CMAKE_86 IF "%1" == "86" GOTO CMAKE_86 IF "%1" == "x64" GOTO CMAKE_64 IF "%1" == "64" GOTO CMAKE_64 ECHO %1 ECHO "Nothing to do" GOTO End :CMAKE_86 ECHO "Configuring for x86" cm86.bat GOTO End :CMAKE_64 ECHO "Configuring for x64" cm64.bat GOTO End :NO_PARAMS ECHO "No parameters specified" IF EXIST "%ProgramW6432%" GOTO CMAKE_64 GOTO CMAKE_86 :End
विंडोज (cm86.bat)
rmdir /S /Q Win mkdir Win cd Win cmake ../ -G "Visual Studio 11" cd ..
विंडोज (cm64.bat)
rmdir /S /Q Win mkdir Win cd Win cmake ../ -G "Visual Studio 11 Win64" cd ..
लिनक्स (cmLinux.sh)
न्यूनतम wxWidgets CMake के साथ आवेदन
आरंभ करने के लिए, हमें एक एप्लिकेशन टेम्पलेट की आवश्यकता होती है जिसमें हम कार्यक्षमता जोड़ेंगे। आइए एक साधारण एप्लिकेशन बनाएं जिसमें एक एप्लिकेशन क्लास (जैसे wxModularHostApp) और एक मुख्य फॉर्म क्लास (जैसे मेनफ्रेम) शामिल हैं।
यदि आप DialogBlocks का उपयोग करते हैं, तो, प्रत्येक वर्ग के लिए h / cpp फ़ाइलों की एक जोड़ी के अलावा, हमें एक और .rc फ़ाइल मिलती है जिसमें एप्लिकेशन संसाधनों का वर्णन होता है।
मैं कोड नहीं दूंगा। एक उदाहरण पिछले लेखों या
%WXWIN%\samples\minimal
फ़ोल्डर से लिया जा सकता है
अब हम CMake स्क्रिप्ट बनाने के लिए तैयार हैं।
wxModularHost / CMakeLists.txt set(SRCS MainFrame.cpp wxModularHostApp.cpp) set(HEADERS MainFrame.h wxModularHostApp.h) set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES}) if(WIN32) set(SRCS ${SRCS} wxModularHost.rc) set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS}; /D_USRDLL; /DwxUSE_NO_MANIFEST=1; /D__STDC_CONSTANT_MACROS) endif(WIN32) set(LIBS ${wxWidgets_LIBRARIES}) set(EXECUTABLE_NAME wxModularHost) add_definitions(${PREPROCESSOR_DEFINITIONS}) include_directories(${INCLUDE_DIRECTORIES}) if(WIN32) set(EXECUTABLE_TYPE WIN32) endif(WIN32) if(APPLE) set(MACOSX_BUNDLE YES) set(EXECUTABLE_TYPE MACOSX_BUNDLE) endif(APPLE) if(LINUX) set(EXECUTABLE_TYPE "") endif(LINUX) set(PROJECT_FILES ${SRCS} ${HFILES}) add_executable(${EXECUTABLE_NAME} ${EXECUTABLE_TYPE} ${PROJECT_FILES}) set(EXE_DIR bin) set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${EXE_DIR}${LIB_SUFFIX}) set_target_properties(${EXECUTABLE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) target_link_libraries(${EXECUTABLE_NAME} ${LIBS})
पहले से तैयार हेडर
संकलन प्रक्रिया को गति देने के लिए, पहले से तैयार हेडर (
http://en.wikipedia.org/wiki/Precompiled_header ) का उपयोग करना संभव है।
इस सुविधा को लागू करने के लिए, हमें दो फ़ाइलों की आवश्यकता है:
शामिल / stdwx.h #ifndef _STDWX_H_ #define _STDWX_H_ #if defined(WIN32) || defined(WINDOWS) #include <windows.h> #include <winnt.h> #define PLUGIN_EXPORTED_API WXEXPORT #else #define PLUGIN_EXPORTED_API extern "C" #endif // SYSTEM INCLUDES // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #include "wx/wx.h" #include <wx/cmdline.h> #include <wx/config.h> #include <wx/defs.h> #include <wx/dir.h> #include <wx/display.h> #include <wx/dynlib.h> #include <wx/dynload.h> #include <wx/fileconf.h> #include <wx/filename.h> #include <wx/frame.h> #include <wx/glcanvas.h> #include <wx/hashmap.h> #include <wx/image.h> #include <wx/imaglist.h> #include <wx/intl.h> #include <wx/list.h> #include <wx/notebook.h> #include <wx/stdpaths.h> #include <wx/sstream.h> #include <wx/thread.h> #include <wx/treebook.h> #include <wx/wfstream.h> #include <wx/wupdlock.h> #include <wx/textfile.h> #include <wx/socket.h> #include <wx/mimetype.h> #include <wx/ipc.h> #endif
शामिल / stdwx.cpp #include "stdwx.h"
सी ++ स्रोत फ़ाइलों के अलावा, हमें विजुअल स्टूडियो प्रोजेक्ट के लिए पूर्वनिर्धारित हेडर के साथ काम करने के लिए आवश्यक नियम जोड़ने के लिए सीएमके को पढ़ाने की भी आवश्यकता है। इसके लिए, एक विशेष मॉड्यूल हमारी मदद करेगा। मुझे याद नहीं है कि यह कहाँ से आया है, लेकिन यह यहाँ से आता है (
http://public.kitware.com/Bug/file_download.php?file_id=901&type=bug )। Precompiled शीर्षकों का समर्थन करने के लिए CMake मॉड्यूल का स्रोत कोड यहां देखा जा सकता है:
https://github.com/T-Rex/wxModularApp/blob/master/build/PCHSupport.cmake ।
इस मॉड्यूल को बिल्ड / CmakeLists.txt में इस तरह शामिल किया जाना चाहिए:
निर्माण / CMakeLists.txt cmake_minimum_required(VERSION 2.6.0) include(PCHSupport.cmake) ...
Precompiled हेडर को प्रोजेक्ट से कनेक्ट करने के बाद, सभी प्रोजेक्ट में पहली लाइन .CPP फाइलें लाइन होनी चाहिए
#include "stdwx.h"
GUI के बिना सबसे सरल प्लगइन
बेस क्लास लाइब्रेरी
प्लगइन के विकास के लिए, और वांछित प्रकार के प्लगइन्स को कुशलता से लोड करने के लिए आवेदन के लिए, आपको कुछ प्रारंभिक कार्य करने की आवश्यकता है। हमें एक पुस्तकालय बनाने की आवश्यकता है जिसमें प्लगइन का आधार सार वर्ग होगा, जिसकी कार्यक्षमता हमें प्रत्येक विशिष्ट प्लगइन में लागू करने की आवश्यकता होगी। इसके अलावा, हमारे मुख्य एप्लिकेशन में इस प्रकार के पॉइंटर्स होंगे, जो पुस्तकालयों के अंदर बनाए गए थे और विशिष्ट प्लगइन कार्यान्वयन के लिए संदर्भित थे।
यानी नतीजतन, हमें एक लाइब्रेरी सीखनी चाहिए जिसमें प्लगइन्स और मुख्य एप्लिकेशन दोनों में उपयोग किए जाने वाले डेटा प्रकार हैं।
wxNonGuiPluginBase / घोषणाएँ #ifndef _DECLARATIONS_H #define _DECLARATIONS_H #if defined(__WXMSW__) #ifdef DEMO_PLUGIN_EXPORTS #define DEMO_API __declspec(dllexport) #else #define DEMO_API __declspec(dllimport) #endif #else #define DEMO_API #endif #endif
wxNonGuiPluginBase / wxNonGuiPluginBase.h #pragma once #include "Declarations.h" class DEMO_API wxNonGuiPluginBase : public wxObject { DECLARE_ABSTRACT_CLASS(wxNonGuiPluginBase) public: wxNonGuiPluginBase(); virtual ~wxNonGuiPluginBase(); virtual int Work() = 0; }; typedef wxNonGuiPluginBase * (*CreatePlugin_function)(); typedef void (*DeletePlugin_function)(wxNonGuiPluginBase * plugin);
Declarations.h फ़ाइल में
DEMO_API
मैक्रो की परिभाषा है, जो इंगित करता है
wxNonGuiPluginBase
वर्ग हमसे निर्यात किया गया है या आयात किया गया है। यह
DEMO_PLUGIN_EXPORTS
प्रीप्रोसेसर
DEMO_PLUGIN_EXPORTS
की उपलब्धता के आधार पर
dllexport/dllimport
(
http://msdn.microsoft.com/en-us/library/3y1sfaz2(v=vs.90).aspx देखें
) का उपयोग करके किया जाता है। जब wxNonGuiPluginBase लाइब्रेरी का निर्माण करते हैं, तो हम
DEMO_PLUGIN_EXPORTS
को प्रीप्रोसेसर निर्देशों की सूची में निर्दिष्ट करते हैं, और जब प्लगिंग निर्माण करते हैं जो wxNonGuiGluginBase लाइब्रेरी पर निर्भर करते हैं और मुख्य एप्लिकेशन का निर्माण करते समय, हम निर्दिष्ट नहीं करते हैं। इस प्रकार, wxNonGuiPluginBase परियोजना के लिए, DEMO_API मान में
dllexport
विशेषता और अन्य सभी परियोजनाओं के लिए
dllimport
मान होगा।
wxNonGuiPluginBase / wxNonGuiPluginBase.cpp #include "stdwx.h" #include "wxNonGuiPluginBase.h" IMPLEMENT_ABSTRACT_CLASS(wxNonGuiPluginBase, wxObject) wxNonGuiPluginBase::wxNonGuiPluginBase() { } wxNonGuiPluginBase::~wxNonGuiPluginBase() { }
wxNonGuiPluginBase / CMakeLists.txt set (SRCS wxNonGuiPluginBase.cpp) set (HEADERS Declarations.h wxNonGuiPluginBase.h) set(LIBRARY_NAME wxNonGuiPluginBase) if(WIN32)
जैसा कि पहले उल्लेख किया गया है,
DEMO_PLUGIN_EXPORTS
मैक्रो में
DEMO_PLUGIN_EXPORTS
मैक्रो की घोषणा है, जिसका उपयोग परिभाषाओं में किया जाता है।
DEMO_PLUGIN_EXPORTS
फ़ाइल
पहला प्लगइन
प्लगइन में, हमें
wxNonGuiPluginBase
से प्राप्त एक वर्ग बनाने की जरूरत है,
wxNonGuiPluginBase
कार्यशील कार्यक्षमता को लागू करना है, और वर्ग का एक उदाहरण बनाने और इसे हटाने के लिए निर्यात किए गए फ़ंक्शन भी करना है। इन कार्यों को मुख्य एप्लिकेशन द्वारा बुलाया जाएगा।
सेम्पलोनगूइपलुगिन / सैंपलनोनगुईपलुगिन.ह #pragma once #include <wxNonGuiPluginBase.h> class SampleNonGuiPlugin : public wxNonGuiPluginBase { DECLARE_DYNAMIC_CLASS(SampleNonGuiPlugin) public: SampleNonGuiPlugin(); virtual ~SampleNonGuiPlugin(); virtual int Work(); };
सेम्पलोनगूइपलुगिन / सैंपलोनगुईपलुगिन.कैप #include "stdwx.h" #include "SampleNonGuiPlugin.h" IMPLEMENT_DYNAMIC_CLASS(SampleNonGuiPlugin, wxObject) SampleNonGuiPlugin::SampleNonGuiPlugin() { } SampleNonGuiPlugin::~SampleNonGuiPlugin() { } int SampleNonGuiPlugin::Work() { return 10; }
सेम्पलोनगूइपलुगिन / सैंपलनोनगुईपलुगिन.डेफ LIBRARY "SampleNonGuiPlugin" EXPORTS CreatePlugin=CreatePlugin DeletePlugin=DeletePlugin
सेम्पलोनगूइपलुगिन / सैंपलनगूइग्लुगिन एक्सपोर्ट्स। पेपर #include "stdwx.h" #include <wxNonGuiPluginBase.h> #include "SampleNonGuiPlugin.h" PLUGIN_EXPORTED_API wxNonGuiPluginBase * CreatePlugin() { return new SampleNonGuiPlugin; } PLUGIN_EXPORTED_API void DeletePlugin(wxNonGuiPluginBase * plugin) { wxDELETE(plugin); }
सेम्पलोनगूइपलुगिन / सीएमकेलिस्ट्स.टैक्स set (SRCS SampleNonGuiPlugin.cpp SampleNonGuiPluginExports.cpp) set (HEADERS SampleNonGuiPlugin.h) set(LIBRARY_NAME SampleNonGuiPlugin) if(WIN32) set(SRCS ${SRCS} ${LIBRARY_NAME}.def) set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D__STDC_CONSTANT_MACROS) set(LINK_DIRECTORIES ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) set(DEMO_LIBS wxNonGuiPluginBase.lib) endif(WIN32) set(SRCS ${SRCS} ${HEADERS} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) add_definitions(${PREPROCESSOR_DEFINITIONS}) include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES} ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase) link_directories(${LINK_DIRECTORIES}) add_library(${LIBRARY_NAME} SHARED ${SRCS}) set(DLL_DIR bin) set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}/${CMAKE_CFG_INTDIR}/plugins) set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) target_link_libraries(${LIBRARY_NAME} ${DEMO_LIBS} ${wxWidgets_LIBRARIES}) add_dependencies(${LIBRARY_NAME} wxNonGuiPluginBase) set_precompiled_header(${LIBRARY_NAME} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp)
DEF फ़ाइल को गलती से स्रोत कोड फ़ाइलों की सूची में शामिल नहीं किया गया है। इसके उपयोग के बिना, निर्यात किए गए कार्यों के नाम को सजाया जाएगा और आवेदन DLL से इन कार्यों के लिए एक संकेतक प्राप्त करने में सक्षम नहीं होगा। आप डीएलएफ से डीएलएफ फ़ाइलों और निर्यात कार्यों का उपयोग करने के बारे में पढ़ सकते हैं:
प्लगइन प्रबंधन मॉड्यूल
इसलिए, इस समय हमारे पास एक होस्ट एप्लिकेशन और एक न्यूनतम प्लगइन है। अब आपको एप्लिकेशन में प्लगइन के डाउनलोड और उपयोग को लागू करने की आवश्यकता है। सार्वभौमिकता के उद्देश्य के लिए, कोड को अलग करना बेहतर होता है जो एक अलग कक्षा में मेमोरी से प्लगइन्स की खोज, लोडिंग और अनलोडिंग से निपटेगा और इससे भी बेहतर - एक अलग लाइब्रेरी में।
हमारे प्लगइन्स के कार्यान्वयन को एक बार फिर से याद करें:
- प्लगइन एक गतिशील पुस्तकालय है
- लाइब्रेरी ने
CreatePlugin()
और DeletePlugin()
फ़ंक्शन निर्यात CreatePlugin()
- सभी प्लगइन कार्यक्षमता गतिशील पुस्तकालय के अंदर इसी वर्ग में लागू किया जाता है, इस वर्ग का एक ऑब्जेक्ट
CreatePlugin()
फ़ंक्शन द्वारा लौटाया जाता है - लाइब्रेरी के अंदर का वर्ग
wxNonGuiPluginBase
सार्वजनिक इंटरफ़ेस को लागू करता है, जिसके बारे में आवेदन जानता है। - लाइब्रेरी को ऑब्जेक्ट के पूरे जीवनकाल में मेमोरी में लोड किया जाना चाहिए जो कि
CreatePlugin()
फ़ंक्शन से प्राप्त होता है - प्लगइन के साथ काम पूरा होने पर, हमें मेमोरी से ऑब्जेक्ट को हटाने की जरूरत है (यह
DeletePlugin()
फ़ंक्शन द्वारा किया गया है) और लाइब्रेरी को मेमोरी से अनलोड करें। - मेमोरी से डेटा को लोड और अनलोड करने के अलावा, एप्लिकेशन को इस उद्देश्य के लिए विशेष रूप से डिज़ाइन किए गए फ़ोल्डर में एक ही प्रकार के प्लग-इन को खोजने में सक्षम होना चाहिए।
इन आवश्यकताओं के आधार पर, हम निम्नलिखित निष्कर्ष पर आ सकते हैं:
- चूंकि एक से अधिक प्लगइन हो सकते हैं, इसलिए आपको मेमोरी में लोड किए गए पुस्तकालयों की सूची को संग्रहीत करने की आवश्यकता है
- चूंकि एक से अधिक पुस्तकालय हो सकते हैं, इसलिए आपको लाइब्रेरी से लोड की गई वस्तुओं की सूची को मेमोरी में स्टोर करने की आवश्यकता है
- चूँकि लाइब्रेरी को पहले से याद नहीं किया जाना चाहिए ताकि संबंधित कार्य ऑब्जेक्ट को हटा दिया जाए, यह आवश्यक है कि किसी तरह इस ऑब्जेक्ट को लाइब्रेरी के पत्राचार को ट्रैक करने की क्षमता प्रदान की जाए।
इन आवश्यकताओं और निष्कर्षों के आधार पर, हम प्लगइन प्रबंधन वर्ग और कंटेनरों को लागू करते हैं:
wxModularCore / wxModularCore.h #pragma once #include <wxNonGuiPluginBase.h> // We need to know which DLL produced the specific plugin object. WX_DECLARE_HASH_MAP(wxNonGuiPluginBase*, wxDynamicLibrary*, wxPointerHash, wxPointerEqual, wxNonGuiPluginToDllDictionary); // We also need to keep the list of loaded DLLs WX_DECLARE_LIST(wxDynamicLibrary, wxDynamicLibraryList); // And separate list of loaded plugins for faster access. WX_DECLARE_LIST(wxNonGuiPluginBase, wxNonGuiPluginBaseList); class wxModularCoreSettings; class wxModularCore { public: wxModularCore(); virtual ~wxModularCore(); virtual wxString GetPluginsPath(bool forceProgramPath) const; virtual wxString GetPluginExt(); bool LoadPlugins(bool forceProgramPath); bool UnloadPlugins(); const wxNonGuiPluginBaseList & GetNonGuiPlugins() const; void Clear(); private: bool LoadNonGuiPlugins(const wxString & pluginsDirectory); bool UnloadNonGuiPlugins(); bool RegisterNonGuiPlugin(wxNonGuiPluginBase * plugin); bool UnRegisterNonGuiPlugin(wxNonGuiPluginBase * plugin); wxDynamicLibraryList m_DllList; wxNonGuiPluginToDllDictionary m_MapNonGuiPluginsDll; wxNonGuiPluginBaseList m_NonGuiPlugins; wxModularCoreSettings * m_Settings; };
कोड पर विस्तार से विचार करें:
- (
wxDynamicLibraryList
), - ( wxNonGuiPluginBaseList
), -, ( wxNonGuiPluginToDllDictionary
) - , , , , ( Windows .dll, Linux OS X .so)
- , - .
- .
m_Settings
. , (, , , , , , , ). .
wxModularCore/wxModularCore.cpp #include "stdwx.h" #include "wxModularCore.h" #include "wxModularCoreSettings.h" #include <wx/listimpl.cpp> WX_DEFINE_LIST(wxDynamicLibraryList); WX_DEFINE_LIST(wxNonGuiPluginBaseList); wxModularCore::wxModularCore() :m_Settings(new wxModularCoreSettings) { // This will allow to delete all objects from this list automatically m_DllList.DeleteContents(true); } wxModularCore::~wxModularCore() { Clear(); wxDELETE(m_Settings); } void wxModularCore::Clear() { UnloadPlugins(); // TODO: Add the code which resets the object to initial state } bool wxModularCore::LoadPlugins(bool forceProgramPath) { wxString pluginsRootDir = GetPluginsPath(forceProgramPath); wxFileName fn; fn.AssignDir(pluginsRootDir); wxLogDebug(wxT("%s"), fn.GetFullPath().data()); fn.AppendDir(wxT("plugins")); wxLogDebug(wxT("%s"), fn.GetFullPath().data()); if (!fn.DirExists()) return false; return LoadNonGuiPlugins(fn.GetFullPath()); } bool wxModularCore::UnloadPlugins() { return UnloadNonGuiPlugins(); } bool wxModularCore::LoadNonGuiPlugins(const wxString & pluginsDirectory) { wxFileName fn; fn.AssignDir(pluginsDirectory); wxLogDebug(wxT("%s"), fn.GetFullPath().data()); fn.AppendDir(wxT("nongui")); wxLogDebug(wxT("%s"), fn.GetFullPath().data()); if (!fn.DirExists()) return false; if(!wxDirExists(fn.GetFullPath())) return false; wxString wildcard = wxString::Format(wxT("*.%s"), GetPluginExt().GetData()); wxArrayString pluginPaths; wxDir::GetAllFiles(fn.GetFullPath(), &pluginPaths, wildcard); for(size_t i = 0; i < pluginPaths.GetCount(); ++i) { wxString fileName = pluginPaths[i]; wxDynamicLibrary * dll = new wxDynamicLibrary(fileName); if (dll->IsLoaded()) { wxDYNLIB_FUNCTION(CreatePlugin_function, CreatePlugin, *dll); if (pfnCreatePlugin) { wxNonGuiPluginBase* plugin = pfnCreatePlugin(); RegisterNonGuiPlugin(plugin); m_DllList.Append(dll); m_MapNonGuiPluginsDll[plugin] = dll; } else wxDELETE(dll); } } return true; } bool wxModularCore::UnloadNonGuiPlugins() { bool result = true; wxNonGuiPluginBase * plugin = NULL; while (m_NonGuiPlugins.GetFirst() && (plugin = m_NonGuiPlugins.GetFirst()->GetData())) { result &= UnRegisterNonGuiPlugin(plugin); } return result; } wxString wxModularCore::GetPluginsPath(bool forceProgramPath) const { wxString path; if (m_Settings->GetStoreInAppData() && !forceProgramPath) path = wxStandardPaths::Get().GetConfigDir(); else path = wxPathOnly(wxStandardPaths::Get().GetExecutablePath()); return path; } wxString wxModularCore::GetPluginExt() { return #if defined(__WXMSW__) wxT("dll"); #else wxT("so"); #endif } bool wxModularCore::RegisterNonGuiPlugin(wxNonGuiPluginBase * plugin) { m_NonGuiPlugins.Append(plugin); return true; } bool wxModularCore::UnRegisterNonGuiPlugin(wxNonGuiPluginBase * plugin) { wxNonGuiPluginBaseList::compatibility_iterator it = m_NonGuiPlugins.Find(plugin); if (it == NULL) return false; do { wxDynamicLibrary * dll = m_MapNonGuiPluginsDll[plugin]; if (!dll) // Probably plugin was not loaded from dll break; wxDYNLIB_FUNCTION(DeletePlugin_function, DeletePlugin, *dll); if (pfnDeletePlugin) { pfnDeletePlugin(plugin); m_NonGuiPlugins.Erase(it); m_MapNonGuiPluginsDll.erase(plugin); return true; } } while (false); // If plugin is not loaded from DLL (eg embedded into executable) wxDELETE(plugin); m_NonGuiPlugins.Erase(it); return true; } const wxNonGuiPluginBaseList & wxModularCore::GetNonGuiPlugins() const { return m_NonGuiPlugins; }
यह एक ऐसी विधि पर ध्यान देने के लिए समझ में आता है LoadNonGuiPlugins()
जिसमें एक मैक्रो की मदद से wxDYNLIB_FUNCTION
हम एक फ़ंक्शन को एक पॉइंटर प्राप्त करते हैं CreatePlugin()
। पॉइंटर प्रकार को CreatePlugin_function
wxNonGuiPluginBase.h में परिभाषित किया गया है।यह उस विधि पर ध्यान देने के लिए भी समझ में आता है UnRegisterNonGuiPlugin()
जिसमें पत्राचार तालिका में प्लगइन की खोज की जाती है; यदि प्लगइन पाया जाता है, तो इसके लिए मिली लाइब्रेरी से एक फ़ंक्शन को बुलाया जाता है DeletePlugin()
और लाइब्रेरी को अनलोड किया जाता है। यदि प्लगइन तालिका में नहीं मिला है (उदाहरण के लिए, इसे एप्लिकेशन में लागू किया गया है और हमने इसे मैन्युअल रूप से सूची में जोड़ा है), तो यह बस मेमोरी से हटा दिया जाता है।wxModularCore / wxModularCoreSettings.h #pragma once class wxModularCoreSettings { public: wxModularCoreSettings(); wxModularCoreSettings(const wxModularCoreSettings & settings); wxModularCoreSettings & operator = (const wxModularCoreSettings & settings); virtual ~wxModularCoreSettings(); void SetStoreInAppData(const bool & val); bool GetStoreInAppData() const; protected: virtual void CopyFrom(const wxModularCoreSettings & settings); private: bool m_bStoreInAppData;
wxModularCore / wxModularCoreSettings.cpp #include "stdwx.h" #include "wxModularCoreSettings.h" wxModularCoreSettings::wxModularCoreSettings() : m_bStoreInAppData(false) { } wxModularCoreSettings::wxModularCoreSettings(const wxModularCoreSettings & settings) { CopyFrom(settings); } wxModularCoreSettings & wxModularCoreSettings::operator = (const wxModularCoreSettings & settings) { if (this != &settings) { CopyFrom(settings); } return *this; } wxModularCoreSettings::~wxModularCoreSettings() { } void wxModularCoreSettings::CopyFrom(const wxModularCoreSettings & settings) { m_bStoreInAppData = settings.m_bStoreInAppData; } void wxModularCoreSettings::SetStoreInAppData(const bool & value) { m_bStoreInAppData = value; } bool wxModularCoreSettings::GetStoreInAppData() const { return m_bStoreInAppData; }
wxModularCore / CMakeLists.txt set (SRCS wxModularCore.cpp wxModularCoreSettings.cpp) set (HEADERS wxModularCore.h wxModularCoreSettings.h) set(LIBRARY_NAME wxModularCore) if(WIN32) set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D__STDC_CONSTANT_MACROS) set(LINK_DIRECTORIES ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) set(DEMO_LIBS wxNonGuiPluginBase.lib) endif(WIN32) set(SRCS ${SRCS} ${HEADERS} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) add_definitions(${PREPROCESSOR_DEFINITIONS}) include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES} ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase) link_directories(${LINK_DIRECTORIES}) add_library(${LIBRARY_NAME} STATIC ${SRCS}) set(DLL_DIR bin) set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}/${CMAKE_CFG_INTDIR}) set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) target_link_libraries(${LIBRARY_NAME} ${DEMO_LIBS} ${wxWidgets_LIBRARIES}) add_dependencies(${LIBRARY_NAME} wxNonGuiPluginBase) set_precompiled_header(${LIBRARY_NAME} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp)
और आपको मुख्य CMakeLists.txt में wxModularCore प्रोजेक्ट का पथ शामिल करना भी याद रखना चाहिए:build / CMakeLists.txt ... add_subdirectory (../wxModularCore ../../wxModularCore/${OS_BASE_NAME}${LIB_SUFFIX}) ...
अनुप्रयोग में GUI के बिना प्लगइन्स का उपयोग करना
प्लगइन्स को प्रबंधित करने वाला वर्ग तैयार हो जाने के बाद, हम एप्लिकेशन में इसका उपयोग शुरू कर सकते हैं।सबसे पहले, wxModularCore
आवेदन वर्ग के लिए एक सूचक क्षेत्र :wxModularHost / wxModularHostApp.h ... class wxModularHostApp: public wxApp { void TestNonGuiPlugins(); ... wxModularCore * m_PluginManager; ... };
wxModularHost / wxModularHostApp.cpp void wxModularHostApp::Init() {
और इस तरह हम प्लगइन लोडिंग विधि को कॉल करेंगे और स्वयं प्लगइन्स का उपयोग करेंगे:wxModularHost / wxModularHostApp.cpp bool wxModularHostApp::OnInit() { ... TestNonGuiPlugins(); MainFrame* mainWindow = new MainFrame( NULL ); mainWindow->Show(true); return true; } int wxModularHostApp::OnExit() { wxDELETE(m_PluginManager);
विधि में, TestNonGuiPlugins()
हम पहले से विधि LoadPlugins()
को कॉल करते हैं wxModularCore
, अगर यह सही तरीके से काम करता है, तो हम प्लगइन्स की सूची से गुजरते हैं और सूची के प्रत्येक तत्व के लिए विधि को कॉल करते हैं Work()
(याद रखें, इसे wxNonGuiPluginBase प्रोजेक्ट में घोषित किया गया है, लेकिन वास्तव में यह प्रत्येक भरी हुई लाइब्रेरी के लिए एक अलग कार्यान्वयन है)।सरलतम जीयूआई प्लगइन
केवल लॉजिक वाले मॉड्यूल कैसे बनाएं, यह पता लगाया गया है। अब एक मॉड्यूल के उदाहरण पर विचार करें जो एक विंडो बनाने की हिम्मत करता है:wxGuiPluginBase / wxGuiPluginBase.h #pragma once #include "Declarations.h" class DEMO_API wxGuiPluginBase : public wxObject { DECLARE_ABSTRACT_CLASS(wxGuiPluginBase) public: wxGuiPluginBase(); virtual ~wxGuiPluginBase(); virtual wxString GetName() const = 0; virtual wxString GetId() const = 0; virtual wxWindow * CreatePanel(wxWindow * parent) = 0; }; typedef wxGuiPluginBase * (*CreateGuiPlugin_function)(); typedef void (*DeleteGuiPlugin_function)(wxGuiPluginBase * plugin);
सार्वजनिक आभासी तरीके:GetName()
- मॉड्यूल का नाम देता हैGetId()
- मॉड्यूल के लिए एक विशिष्ट पहचानकर्ता लौटाता है (आप इसके लिए GUID का उपयोग कर सकते हैं, विजुअल स्टूडियो में इस उद्देश्य के लिए एक विशेष उपयोगिता है। मेनू देखें Tools -> Create GUID
)CreatePanel()
- एक नियंत्रण बनाता है (कोई भी नियंत्रण प्रदर्शन के लिए हमारे अनुकूल होगा) और इसके लिए एक संकेतक लौटाता है।
इस इंटरफ़ेस के आधार पर प्लगिन कार्यान्वयन:सेम्पलगुइप्लगिन 1 / सैंपलगुईपलुगिन 1। एच #pragma once #include <wxGuiPluginBase.h> class SampleGuiPlugin1 : public wxGuiPluginBase { DECLARE_DYNAMIC_CLASS(SampleGuiPlugin1) public: SampleGuiPlugin1(); virtual ~SampleGuiPlugin1(); virtual wxString GetName() const; virtual wxString GetId() const; virtual wxWindow * CreatePanel(wxWindow * parent); };
सेम्पलगुइपलुगिन 1 / सैंपलग्यप्लुगिन 1।Cpp #include "stdwx.h" #include "SampleGuiPlugin1.h" IMPLEMENT_DYNAMIC_CLASS(SampleGuiPlugin1, wxObject) SampleGuiPlugin1::SampleGuiPlugin1() { } SampleGuiPlugin1::~SampleGuiPlugin1() { } wxString SampleGuiPlugin1::GetName() const { return _("GUI Plugin 1"); } wxString SampleGuiPlugin1::GetId() const { return wxT("{4E97DF66-5FBB-4719-AF17-76C1C82D3FE1}"); } wxWindow * SampleGuiPlugin1::CreatePanel(wxWindow * parent) { wxWindow * result= new wxPanel(parent, wxID_ANY); result->SetBackgroundColour(*wxRED); return result; }
इस प्लगइन के लिए CMakeLists.txt लगभग वही है जो हमने GUI के बिना प्लगइन के लिए लिखा था। अंतर केवल परियोजना के नाम पर और परियोजना में शामिल फाइलों की सूची में होगा।प्लगइन प्रबंधन मॉड्यूल Refactoring
फिलहाल, हमारे पास दो प्रकार के प्लगइन्स हैं। GUI के बिना प्लग इन के लिए, प्लगइन प्रबंधन वर्ग में पुस्तकालयों को लोड करने, प्लग इन को पंजीकृत करने, प्लग इन को अक्षम करने के लिए एक विशेष तरीका है। इस दृष्टिकोण के साथ, हमें प्रत्येक प्रकार के प्लगइन के लिए इन सभी तरीकों की नकल करने की आवश्यकता होगी। और उनमें से 5-10 होंगे, तब वर्ग अनुचित रूप से आकार में बढ़ेगा। इसलिए, तरीकों LoadXXXPlugins()
, UnloadXXXPlugins()
, RegisterXXXPlugin()
, UnRegisterXXXPlugin()
यह टेम्पलेट्स बनाने का फैसला किया गया था, सूचियों और हैश तालिकाओं एक वर्ग में खड़े उस वर्ग से विरासत में मिली wxModularCore
है कि कोड है कि हमारे आवेदन के लिए विशिष्ट है शामिल होंगे।wxModularCore / wxModularCore.h #pragma once
wxModularHost / SampleModularCore.h #pragma once #include <wxModularCore.h> #include <wxNonGuiPluginBase.h> #include <wxGuiPluginBase.h> // We need to know which DLL produced the specific plugin object. WX_DECLARE_HASH_MAP(wxNonGuiPluginBase*, wxDynamicLibrary*, wxPointerHash, wxPointerEqual, wxNonGuiPluginToDllDictionary); WX_DECLARE_HASH_MAP(wxGuiPluginBase*, wxDynamicLibrary*, wxPointerHash, wxPointerEqual, wxGuiPluginToDllDictionary); // And separate list of loaded plugins for faster access. WX_DECLARE_LIST(wxNonGuiPluginBase, wxNonGuiPluginBaseList); WX_DECLARE_LIST(wxGuiPluginBase, wxGuiPluginBaseList); class SampleModularCore : public wxModularCore { public: virtual ~SampleModularCore(); virtual bool LoadAllPlugins(bool forceProgramPath); virtual bool UnloadAllPlugins(); const wxNonGuiPluginBaseList & GetNonGuiPlugins() const; const wxGuiPluginBaseList & GetGuiPlugins() const; private: wxNonGuiPluginToDllDictionary m_MapNonGuiPluginsDll; wxNonGuiPluginBaseList m_NonGuiPlugins; wxGuiPluginToDllDictionary m_MapGuiPluginsDll; wxGuiPluginBaseList m_GuiPlugins; };
wxModularHost / SampleModularCore.cpp #include "stdwx.h" #include "SampleModularCore.h" #include <wx/listimpl.cpp> WX_DEFINE_LIST(wxNonGuiPluginBaseList); WX_DEFINE_LIST(wxGuiPluginBaseList); SampleModularCore::~SampleModularCore() { Clear(); } bool SampleModularCore::LoadAllPlugins(bool forceProgramPath) { wxString pluginsRootDir = GetPluginsPath(forceProgramPath); bool result = true; result &= LoadPlugins<wxNonGuiPluginBase, wxNonGuiPluginBaseList, wxNonGuiPluginToDllDictionary, CreatePlugin_function>(pluginsRootDir, m_NonGuiPlugins, m_MapNonGuiPluginsDll, wxT("nongui")); result &= LoadPlugins<wxGuiPluginBase, wxGuiPluginBaseList, wxGuiPluginToDllDictionary, CreateGuiPlugin_function>(pluginsRootDir, m_GuiPlugins, m_MapGuiPluginsDll, wxT("gui")); // You can implement other logic which takes in account // the result of LoadPlugins() calls return true; } bool SampleModularCore::UnloadAllPlugins() { return UnloadPlugins<wxNonGuiPluginBase, wxNonGuiPluginBaseList, wxNonGuiPluginToDllDictionary, DeletePlugin_function>(m_NonGuiPlugins, m_MapNonGuiPluginsDll) && UnloadPlugins<wxGuiPluginBase, wxGuiPluginBaseList, wxGuiPluginToDllDictionary, DeleteGuiPlugin_function>(m_GuiPlugins, m_MapGuiPluginsDll); } const wxNonGuiPluginBaseList & SampleModularCore::GetNonGuiPlugins() const { return m_NonGuiPlugins; } const wxGuiPluginBaseList & SampleModularCore::GetGuiPlugins() const { return m_GuiPlugins; }
टेम्पलेट विधियों को लागू करने के बाद, जीयूआई प्लगइन्स के लिए समर्थन जोड़ने में काफी थोड़ा कोड लिया गया।एक आवेदन में जीयूआई प्लगइन्स का उपयोग करना
आवेदन में, हमारे पास मुख्य पैनल के रूप में डॉकिंग विंडो मैनेजर और wxAuiNotebook है। विचार करें कि आप इस wxAuiNotebook पर प्लगइन्स से नियंत्रण कैसे जोड़ सकते हैं:wxModularHost / MainFrame.cpp void MainFrame::AddPagesFromGuiPlugins() { SampleModularCore * pluginManager = wxGetApp().GetPluginManager(); for(wxGuiPluginBaseList::Node * node = pluginManager->GetGuiPlugins().GetFirst(); node; node = node->GetNext()) { wxGuiPluginBase * plugin = node->GetData(); if(plugin) { wxWindow * page = plugin->CreatePanel(m_Notebook); if(page) { m_Notebook->AddPage(page, plugin->GetName()); } } } }
नतीजतन, हमें ऐसी टैब विंडो मिलती है:
टैब हेडर GetName()
प्रत्येक प्लग-इन की विधि से लिया जाता है, टैब स्वयं CreatePanel()
प्लग-इन विधि द्वारा बनाए जाते हैं ।लिनक्स के लिए सुधार सीएमके-स्क्रिप्ट
विंडोज में, हमने फ़ोल्डर को निर्दिष्ट करने के लिए सेटिंग्स का उपयोग करके निर्दिष्ट किया है जिसमें डायनामिक लाइब्रेरी एकत्र की जाएंगी RUNTIME_OUTPUT_DIRECTORY
। लिनक्स में, के रूप में प्लगइन एक गतिशील पुस्तकालय (अर्थात् एक पुस्तकालय) है, सेटिंग का उपयोग किया जाता है LIBRARY_OUTPUT_DIRECTORY
। लेकिन यहां हमें एक समस्या का सामना करना पड़ रहा है: यदि आप बिन फ़ोल्डर के अंदर सीधे पुस्तकालयों का निर्माण करते हैं, तो आश्रित परियोजनाओं के निर्माण के दौरान लिंकर को यह पुस्तकालय नहीं मिलेगा। इन उद्देश्यों के लिए, आपको एक स्क्रिप्ट जोड़ने की आवश्यकता है जो लाइब्रेरी बनाने के बाद बाहर काम करेगी और इसे बिन फ़ोल्डर के अंदर सही जगह पर कॉपी कर सकती है। यह सभी गतिशील पुस्तकालयों (मूल और प्लग-इन दोनों के लिए) के लिए किए जाने की आवश्यकता होगी:SampleGuiPlugin2 / CMakeLists.txt ... set(DLL_DIR bin) if(LINUX) set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}/plugins/nongui) else(LINUX) set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}/${CMAKE_CFG_INTDIR}/plugins/nongui) get_target_property(RESULT_FULL_PATH ${LIBRARY_NAME} LOCATION) get_filename_component(RESULT_FILE_NAME ${RESULT_FULL_PATH} NAME) endif(LINUX) set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) ... if(LINUX) add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${TARGET_LOCATION} COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${LIBRARY_NAME}> ${TARGET_LOCATION}/${RESULT_FILE_NAME} ) endif(LINUX)
लिनक्स पर सभी प्लगइन्स के लिए, हमें निर्भरता की एक सूची भी निर्दिष्ट करनी होगी:SampleGuiPlugin2 / CMakeLysts.txt ... if(WIN32) set(SRCS ${SRCS} ${LIBRARY_NAME}.def) set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D__STDC_CONSTANT_MACROS) set(LINK_DIRECTORIES ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) set(DEMO_LIBS wxNonGuiPluginBase.lib) endif(WIN32) if(LINUX) set(DEMO_LIBS wxNonGuiPluginBase) endif(LINUX) ...
सब कुछ ठीक लग रहा है, प्रोजेक्ट चल रहा है। लेकिन जब हम एप्लिकेशन को शुरू करने का प्रयास करते हैं, तो हम पाते हैं कि डायनेमिक लाइब्रेरी लोड नहीं होती हैं (यदि आप पहले से निर्मित एप्लिकेशन को किसी अन्य मशीन में ट्रांसफर करते हैं तो यह एक अप्रिय आश्चर्य हो सकता है)।और सभी क्योंकि पुस्तकालय के अंदर निर्माण करते समय, पूर्ण पथ वाले निर्भरता के लिंक पंजीकृत होते हैं। इसलिए, उदाहरण के लिए, प्रत्येक प्लगइन के लिए बेस कक्षाओं के साथ पुस्तकालय का पूरा रास्ता इंगित किया जाएगा, जो अन्य कामकाजी मशीन पर नहीं होगा। आप ldd उपयोगिता का उपयोग करके इसे सत्यापित कर सकते हैं: ldd libSampleGuiPlugin2.so | grep wxSampleGuiPluginBase
पूर्ण पथ से छुटकारा पाने के लिए, CMake में आपको RPATH
पुस्तकालयों के लिए एक विकल्प निर्दिष्ट करने की आवश्यकता है जो हमारे समाधान से अन्य गतिशील पुस्तकालयों का संदर्भ देते हैं:SampleGuiPlugin2 / CMakeLists.txt if(WIN32) set(SRCS ${SRCS} ${LIBRARY_NAME}.def) set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D__STDC_CONSTANT_MACROS) set(LINK_DIRECTORIES ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) set(DEMO_LIBS wxNonGuiPluginBase.lib) endif(WIN32) if(LINUX) set(DEMO_LIBS wxNonGuiPluginBase) SET(CMAKE_SKIP_BUILD_RPATH FALSE) SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) SET(CMAKE_INSTALL_RPATH ".:./../../") endif(LINUX)
क्योंकि
प्लगइन उपनिर्देशिका प्लगइन्स / गुई में स्थित है, तो wxGuiPluginBase लाइब्रेरी को दो स्तरों पर खोजा जाना चाहिए, जैसा कि CMakeLists.txt में दर्शाया गया हैOS X के लिए CMake स्क्रिप्ट में सुधार करना
लिनक्स की तरह, ओएस एक्स में हमें प्लगइन्स के लिए निर्भरता लोड करने की समस्या है। OS X में, डायनेमिक लाइब्रेरी के पथ को ठीक करने के लिए, आप install_name_tool उपयोगिता का उपयोग कर सकते हैं।चलो CMakeLists.txt में कोड जोड़ते हैं, जो पुस्तकालय पथ को रिश्तेदार के साथ बदल देता है:SampleGuiPlugin2 / CMakeLists.txt if(APPLE) FOREACH(DEP_LIB ${DEMO_LIBS}) get_filename_component(ABS_ROOT_DIR ${PROJECT_ROOT_DIR} ABSOLUTE) set(LIBNAME_FULL "${ABS_ROOT_DIR}/${DEP_LIB}/${OS_BASE_NAME}${LIB_SUFFIX}/$(CONFIGURATION)/lib${DEP_LIB}.dylib") add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD COMMAND install_name_tool -change "${LIBNAME_FULL}" "@executable_path/../Frameworks/lib${DEP_LIB}.dylib" $<TARGET_FILE:${LIBRARY_NAME}>) ENDFOREACH(DEP_LIB) endif(APPLE)
एप्लिकेशन की CMake स्क्रिप्ट में, आपको wxModularHost / CMakeLists.txt पर समान परिवर्तन करने कीआवश्यकता है if(APPLE) FOREACH(DEP_LIB ${DEMO_LIBS_SHARED}) get_filename_component(ABS_ROOT_DIR ${PROJECT_ROOT_DIR} ABSOLUTE) set(LIBNAME_FULL "${ABS_ROOT_DIR}/${DEP_LIB}/${OS_BASE_NAME}${LIB_SUFFIX}/$(CONFIGURATION)/lib${DEP_LIB}.dylib") add_custom_command(TARGET ${EXECUTABLE_NAME} POST_BUILD COMMAND install_name_tool -change "${LIBNAME_FULL}" "@executable_path/../Frameworks/lib${DEP_LIB}.dylib" $<TARGET_FILE:${EXECUTABLE_NAME}>) ENDFOREACH(DEP_LIB) endif(APPLE)
निष्कर्ष में
हमने क्रॉस-प्लेटफ़ॉर्म मॉड्यूलर अनुप्रयोगों को कैसे बनाया जाए, साथ ही सीएमके का उपयोग करके विंडोज और ओएस एक्स मेकफाइल के लिए प्रोजेक्ट बनाने की प्रक्रिया को देखा। मुझे आशा है कि किसी को यह सामग्री उपयोगी लगेगी।लेख में चर्चा की गई परियोजना का पूर्ण स्रोत कोड GitHub: https://github.com/T-Rex/wxModularAppPS पर पाया जा सकता है: लेख के लेखन के दौरान, wxWidgets (3.0) का एक नया संस्करण जारी किया गया है, जिसके साथ CMake अभी भी काम नहीं कर सकता है (द्वारा) 2.9.x के लिए काम करने वाली कम से कम स्क्रिप्ट 3.0 से काम नहीं करती है। परीक्षण के लिए, शाखा 2.9 से कोड का उपयोग करना बेहतर होता है: svn.wxwidgets.org/svn/wx/wxWidgets/tags/WX5_9_5