गतिशील प्रेषण
कंप्यूटर विज्ञान में, गतिशील प्रेषण एक बहुरूपता (कंप्यूटर विज्ञान) संचालन (विधि (कंप्यूटर प्रोग्रामिंग) या फ़ंक्शन) के कार्यान्वयन को रन टाइम (प्रोग्राम जीवनचक्र चरण) पर कॉल करने की प्रक्रिया है। यह सामान्यतः ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग (OOP) भाषाओं और प्रणालियों में कार्यरत है, और इसकी एक प्रमुख विशेषता मानी जाती है।[1]
ऑब्जेक्ट-ओरिएंटेड सिस्टम एक समस्या को अंतःक्रिया ऑब्जेक्ट्स के एक सेट के रूप में मॉडल करते हैं जो नाम से संदर्भित संचालन को लागू करते हैं। बहुरूपता वह परिघटना है जिसमें कुछ विनिमेय वस्तुएं प्रत्येक एक ही नाम के एक संचालन को उजागर करती हैं लेकिन संभवतः व्यवहार में भिन्न होती हैं। एक उदाहरण के रूप में, ए फाइल वस्तु और ए डेटाबेस वस्तु दोनों में एक है स्टोर रिकॉर्ड विधि जिसका उपयोग भंडारण के लिए कार्मिक रिकॉर्ड लिखने के लिए किया जा सकता है। उनके कार्यान्वयन भिन्न हैं। एक प्रोग्राम एक वस्तु के लिए एक संदर्भ रखता है जो या तो एक फाइल हो सकता है फाइल वस्तु या ए डेटाबेस वस्तु यह कुछ भी हो सकता है, एक रन-टाइम सेटिंग द्वारा निर्धारित किया गया हो, और इस स्तर पर, प्रोग्राम को पता नहीं हो सकता है या परवाह नहीं है। जब प्रोग्राम कॉल करता है तो स्टोर रिकॉर्ड वस्तु पर, कुछ को चुनने की जरूरत है कि कौन सा व्यवहार अधिनियमित हो। यदि कोई OOP को ऑब्जेक्ट को भेजे जाने वाले संदेश के रूप में सोचता है, तो इस उदाहरण में प्रोग्राम एक भेजता है, स्टोर रिकॉर्ड किसी अज्ञात प्रकार के ऑब्जेक्ट को संदेश को सही ऑब्जेक्ट पर भेजने के लिए इसे रन-टाइम सपोर्ट सिस्टम पर छोड़ देता है। वस्तु जो भी व्यवहार लागू करती है।[2]
गतिशील प्रेषण स्थिर प्रेषण के विपरीत है, जिसमें एक बहुरूपी संचालन के कार्यान्वयन को संकलन समय पर चुना जाता है। गतिशील प्रेषण का उद्देश्य एक उपयुक्त कार्यान्वयन के चयन को तब तक टाला जाता है जब तक कि रन टाइम प्रकार का पैरामीटर (या एकाधिक पैरामीटर) ज्ञात न हो जाए। गतिशील विधि प्रेषण एक ऐसा तरीका है, जो प्रोग्राम के रनटाइम में ये तय करता है कि किसी ऑब्जेक्ट के रेफ़्रेन्स में कौनसा मेथड एक्सेक्यूटे होगा ना कि कम्पाइल टाइम में गतिशील विधि प्रेषण इसीलिए महत्वपूर्ण है क्योंकि यही वह तरीका है जिससे जावा रन टाइम बहुरूपता को कार्यान्वित करता है।
गतिशील प्रेषण लेट बाइंडिंग (गतिशील बाइंडिंग के रूप में भी जाना जाता है) से अलग है। नाम बाइंडिंग एक नाम को एक संचालन के साथ जोड़ता है। एक बहुरूपी संचालन में कई कार्यान्वयन होते हैं, जो सभी एक ही नाम से जुड़े होते हैं। बाइंडिंग को संकलित समय पर या (देर से बाइंडिंग के साथ) रन टाइम पर बनाया जा सकता है। गतिशील प्रेषण के साथ, संचालन के एक विशेष कार्यान्वयन को रन टाइम पर चुना जाता है। जबकि गतिशील प्रेषण में लेट बाइंडिंग नहीं होती है, लेट बाइंडिंग गतिशील प्रेषण का संकेत देती है, क्योंकि लेट-बाउंड संचालन के कार्यान्वयन को रन टाइम तक नहीं जाना जाता है।[citation needed]
सिंगल और मल्टीपल प्रेषण
कॉल करने की विधि के किसी संस्करण का विकल्प या तो एक वस्तु पर या वस्तुओं के संयोजन पर आधारित हो सकता है। पहले वाले को सिंगल प्रेषण कहा जाता है और यह सामान्य ऑब्जेक्ट-ओरिएंटेड भाषाओं जैसे स्मॉलटाक, C++, जावा (प्रोग्रामिंग भाषा), सी शार्प (प्रोग्रामिंग लैंग्वेज), सी#, ऑब्जेक्टिव सी, स्विफ्ट (प्रोग्रामिंग भाषा), जावास्क्रिप्ट और सीधे समर्थित है। पायथन (प्रोग्रामिंग भाषा) इन और इसी तरह की भाषाओं में, एक समान सिंटैक्स के साथ डिवीजन (गणित) के लिए एक विधि कह सकते हैं
dividend.divide(divisor) # dividend / divisor
जहां पैरामीटर वैकल्पिक हैं। इसे डिविडेंड के लिए पैरामीटर डिवाइडर के साथ डिवाइड नामक संदेश भेजने के रूप में माना जाता है। विभाजक के प्रकार या मूल्य की उपेक्षा करते हुए केवल लाभांश के प्रकार (शायद तर्कसंगत, फ्लोटिंग पॉइंट, मैट्रिक्स) के आधार पर एक कार्यान्वयन चुना जाएगा।
इसके विपरीत, कुछ भाषाएँ ऑपरेंड के संयोजन के आधार पर विधियों या कार्यों को प्रेषित करती हैं; विभाजन प्रकरण में, लाभांश और भाजक के प्रकार एक साथ निर्धारित करते हैं कि कौन सा विभाजन संचालन किया जाएगा। इसे एकाधिक प्रेषण के रूप में जाना जाता है। एकाधिक प्रेषण का समर्थन करने वाली भाषाओं के उदाहरण कॉमन लिस्प, डायलन और जूलिया हैं।
गतिशील प्रेषण तंत्र
विभिन्न गतिशील प्रेषण तंत्रों के साथ एक भाषा लागू की जा सकती है। किसी भाषा द्वारा प्रदान किए गए गतिशील प्रेषण तंत्र के विकल्प काफी सीमा तक उन प्रोग्रामिंग प्रतिमानों को बदल देते हैं जो किसी भाषा में उपलब्ध हैं या उपयोग करने के लिए सबसे स्वाभाविक हैं।
सामान्यतः, एक टाइप की गई भाषा में, प्रेषण तंत्र तर्कों के प्रकार के आधार पर किया जाएगा (सामान्यतः संदेश के प्राप्तकर्ता के प्रकार के आधार पर)। कमजोर या कोई टाइपिंग सिस्टम वाली भाषाएं प्रायः प्रत्येक वस्तु के लिए वस्तु डेटा के भाग के रूप में एक प्रेषण तालिका ले जाती हैं। यह त्वरित व्यवहार की अनुमति देता है क्योंकि प्रत्येक त्वरित किसी दिए गए संदेश को एक अलग विधि में मैप कर सकता है।
कुछ भाषाएँ हाइब्रिड दृष्टिकोण प्रदान करती हैं।
गतिशील प्रेषण में सदैव एक ओवरहेड होता है इसलिए कुछ भाषाएँ विशेष तरीकों के लिए स्थैतिक प्रेषण की पेशकश करती हैं।
C++ कार्यान्वयन
C++ बाइंडिंग का उपयोग करता है और गतिशील और स्थैतिक प्रेषण दोनों प्रदान करता है। प्रेषण का सामान्य रूप स्थिर है। गतिशील प्रेषण प्राप्त करने के लिए प्रोग्रामर को एक वर्चुअल विधि घोषित करनी चाहिए।
C++ कंपाइलर्स सामान्यतः वर्चुअल विधि तालिका (वी टेबल) नामक डेटा संरचना के साथ गतिशील प्रेषण को कार्यान्वित करते हैं जो किसी दिए गए वर्ग के लिए सदस्य फ़ंक्शन पॉइंटर्स के सेट के रूप में नाम-से-कार्यान्वयन मैपिंग को परिभाषित करता है। (यह विशुद्ध रूप से एक कार्यान्वयन विवरण है; C ++ विनिर्देश वी टेबलs का उल्लेख नहीं करता है।) उस प्रकार के उदाहरण तब इस तालिका में उनके उदाहरण डेटा के भाग के रूप में एक सूचक को संग्रहीत करेंगे। यह जटिल है जब एकाधिक वंशानुक्रम का उपयोग किया जाता है। चूंकि C++ देर से बाध्यकारी का समर्थन नहीं करता है, C++ ऑब्जेक्ट में वर्चुअल टेबल को रन-टाइम पर संशोधित नहीं किया जा सकता है, जो प्रेषण लक्ष्य के संभावित सेट को संकलन समय पर चुने गए सीमित सेट तक सीमित करता है।
टाइप ओवरलोडिंग C++ में गतिशील प्रेषण का उत्पादन नहीं करता है क्योंकि भाषा संदेश पैरामीटर के प्रकारों को औपचारिक संदेश नाम का हिस्सा मानती है। इसका मतलब यह है कि प्रोग्रामर जो संदेश नाम देखता है वह बाइंडिंग के लिए उपयोग किया जाने वाला औपचारिक नाम नहीं है।
Go और Rust कार्यान्वयन
Go (प्रोग्रामिंग भाषा), Rust (प्रोग्रामिंग भाषा) और निम (प्रोग्रामिंग भाषा) में, प्रारम्भिक बाइंडिंग के अधिक बहुमुखी रूपांतर का उपयोग किया जाता है।
वी टेबल पॉइंटर्स को ऑब्जेक्ट रेफरेंस के साथ 'फैट पॉइंटर्स' (गो में 'अंतरापृष्ठ', या रस्ट में 'ट्रेट ऑब्जेक्ट्स') के रूप में ले जाया जाता है।[citation needed]
यह अंतर्निहित डेटा संरचनाओं से समर्थित अंतरापृष्ठ को अलग करता है। प्रत्येक संकलित लाइब्रेरी को किसी प्रकार का सही ढंग से उपयोग करने के लिए समर्थित अंतरापृष्ठ की पूरी श्रृंखला को जानने की आवश्यकता नहीं है, केवल विशिष्ट व्यवहार्य लेआउट जिसकी उन्हें आवश्यकता होती है। कोड अलग-अलग अंतरापृष्ठ को अलग-अलग कार्यों के लिए डेटा के एक ही टुकड़े में पास कर सकता है। यह बहुमुखी प्रतिभा प्रत्येक वस्तु संदर्भ के साथ अतिरिक्त डेटा की कीमत पर आती है, जो कि समस्याग्रस्त है यदि ऐसे कई संदर्भों को लगातार संग्रहीत किया जाता है।फैट पॉइंटर शब्द केवल अतिरिक्त संबंधित जानकारी के साथ एक पॉइंटर (कंप्यूटर प्रोग्रामिंग) को संदर्भित करता है। अतिरिक्त जानकारी ऊपर वर्णित गतिशील प्रेषण के लिए एक व्यवहार्य सूचक हो सकती है, लेकिन अधिक सामान्य रूप से संबंधित वस्तु का आकार वर्णन करने के लिए एक टुकड़ा है।[citation needed]
स्मालटॉक कार्यान्वयन
स्मॉलटाक एक प्रकार-आधारित संदेश प्रेषण का उपयोग करता है। प्रत्येक उदाहरण में एक ही प्रकार होता है जिसकी परिभाषा में विधियाँ होती हैं। जब एक उदाहरण एक संदेश प्राप्त करता है, तो प्रेषण प्रकार के लिए संदेश-टू-मेथड मैप में संबंधित विधि को देखता है और फिर विधि को आमंत्रित करता है।
क्योंकि एक प्रकार में आधार प्रकार की श्रृंखला हो सकती है, यह लुक-अप महंगा हो सकता है। स्मॉलटाक के तंत्र का एक सरल कार्यान्वयन C++ की तुलना में काफी अधिक ओवरहेड प्रतीत होता है और यह ओवरहेड प्रत्येक ऑब्जेक्ट को प्राप्त होने वाले प्रत्येक संदेश के लिए खर्च किया जाएगा।
रियल स्मॉलटाक कार्यान्वयन प्रायः इनलाइन कैशिंग नामक तकनीक का उपयोग करते हैं[3]यह विधि प्रेषण को बहुत तेज़ बनाता है। इनलाइन कैशिंग मूल रूप से कॉल साइट के पिछले डेस्टिनेशन मेथड एड्रेस और ऑब्जेक्ट क्लास (या मल्टी-वे कैशिंग के लिए कई जोड़े) को स्टोर करता है। विधि चयनकर्ता के आधार पर कैश्ड विधि को सबसे आम लक्ष्य विधि (या केवल कैश मिस हैंडलर) के साथ प्रारंभ किया गया है। जब विधि कॉल साइट निष्पादन के दौरान पहुंच जाती है, तो यह केवल कैश में पते को कॉल करती है। (गतिशील कोड जनरेटर में, यह कॉल एक डायरेक्ट कॉल है क्योंकि डायरेक्ट एड्रेस कैश मिस लॉजिक द्वारा वापस पैच किया जाता है।) कॉल किए गए तरीके में प्रोलॉग कोड फिर कैश्ड क्लास की तुलना वास्तविक ऑब्जेक्ट क्लास से करता है, और यदि वे मेल नहीं खाते हैं , कक्षा में सही विधि खोजने के लिए एक कैश मिस हैंडलर को निष्पादन शाखाएँ। एक तेज़ कार्यान्वयन में कई कैश प्रविष्टियाँ हो सकती हैं और प्रारंभिक कैश मिस पर सही तरीके से निष्पादन प्राप्त करने के लिए प्रायः केवल कुछ निर्देशों की आवश्यकता होती है। सामान्य मामला एक कैश्ड क्लास मैच होगा, और विधि में निष्पादन जारी रहेगा।
ऑब्जेक्ट क्लास और मेथड सेलेक्टर का उपयोग करके आउट-ऑफ-लाइन कैशिंग का उपयोग मेथड इनवोकेशन लॉजिक में भी किया जा सकता है। एक डिज़ाइन में, वर्ग और विधि चयनकर्ता को हैश किया जाता है, और एक विधि प्रेषण कैश तालिका में एक अनुक्रमणिका के रूप में उपयोग किया जाता है।
जैसा कि स्मॉलटाक एक चिंतनशील भाषा है, कई कार्यान्वयन अलग-अलग वस्तुओं को गतिशील रूप से उत्पन्न विधि लुकअप तालिकाओं के साथ वस्तुओं में बदलने की अनुमति देते हैं। यह वस्तु के व्यवहार को प्रति वस्तु के आधार पर बदलने की अनुमति देता है। प्रोटोटाइप-आधारित प्रोग्रामिंग के रूप में जानी जाने वाली भाषाओं की एक पूरी श्रेणी। प्रोटोटाइप-आधारित भाषाएँ इससे विकसित हुई हैं, जिनमें से सबसे प्रसिद्ध स्वयं (प्रोग्रामिंग भाषा) और जावास्क्रिप्ट हैं। विधि प्रेषण कैशिंग का सावधानीपूर्वक डिज़ाइन प्रोटोटाइप-आधारित भाषाओं को भी उच्च-प्रदर्शन विधि प्रेषण की अनुमति देता है।
पाइथन (प्रोग्रामिंग लैंग्वेज), रूबी (प्रोग्रामिंग भाषा), ऑब्जेक्टिव-सी और ग्रूवी (प्रोग्रामिंग भाषा) सहित कई अन्य गतिशील रूप से टाइप की गई भाषाएँ समान दृष्टिकोण का उपयोग करती हैं।
पायथन में उदाहरण
class Cat:
def speak(self):
print("Meow")
class Dog:
def speak(self):
print("Woof")
def speak(pet):
# Dynamically dispatches the speak method
# pet can either be an instance of Cat or Dog
pet.speak()
cat = Cat()
speak(cat)
dog = Dog()
speak(dog)
C++ में उदाहरण
#include <iostream>
// make Pet an abstract virtual base class
class Pet {
public:
virtual void speak() = 0;
};
class Dog : public Pet {
public:
void speak() override
{
std::cout << "Woof!\n";
}
};
class Cat : public Pet {
public:
void speak() override
{
std::cout << "Meow!\n";
}
};
// speak() will be able to accept anything deriving from Pet
void speak(Pet& pet)
{
pet.speak();
}
int main()
{
Dog fido;
Cat simba;
speak(fido);
speak(simba);
return 0;
}
यह भी देखें
- समारोह बहु-संस्करण
- फंक्शन ओवरलोडिंग
- संदेश देना
- ओवरराइडिंग विधि
- दोहरा प्रेषण
- नाम बाइंडिंग
संदर्भ
- ↑ Milton, Scott; Schmidt, Heinz W. (1994). Dynamic Dispatch in Object-Oriented Languages (Technical report). Vol. TR-CS-94-02. Australian National University. CiteSeerX 10.1.1.33.4292.
- ↑ Driesen, Karel; Hölzle, Urs; Vitek, Jan (1995). "Message Dispatch on Pipelined Processors". ECOOP’95 — Object-Oriented Programming, 9th European Conference, Åarhus, Denmark, August 7–11, 1995. Lecture Notes in Computer Science. Vol. 952. Springer. CiteSeerX 10.1.1.122.281. doi:10.1007/3-540-49538-X_13. ISBN 3-540-49538-X.
- ↑ Müller, Martin (1995). Message Dispatch in Dynamically-Typed Object-Oriented Languages (Master thesis). University of New Mexico. pp. 16–17. CiteSeerX 10.1.1.55.1782.
अग्रिम पठन
- Lippman, Stanley B. (1996). Inside the C++ Object Model. Addison-Wesley. ISBN 0-201-83454-5.
- Groeber, Marcus; Di Geronimo, Jr., Edward "Ed"; Paul, Matthias R. (2002-03-02) [2002-02-24]. "GEOS/NDO info for RBIL62?". Newsgroup: comp.os.geos.programmer. Archived from the original on 2019-04-20. Retrieved 2019-04-20.
[…] The reason Geos needs 16 interrupts is because the scheme is used to convert inter-segment ("far") function calls into interrupts, without changing the size of the code. The reason this is done so that "something" (the kernel) can hook itself into every inter-segment call made by a Geos application and make sure that the proper code segments are loaded from virtual memory and locked down. In DOS terms, this would be comparable to an overlay loader, but one that can be added without requiring explicit support from the compiler or the application. What happens is something like this: […] 1. The real mode compiler generates an instruction like this: CALL <segment>:<offset> -> 9A <offlow><offhigh><seglow><seghigh> with <seglow><seghigh> normally being defined as an address that must be fixed up at load time depending on the address where the code has been placed. […] 2. The Geos linker turns this into something else: INT 8xh -> CD 8x […] DB <seghigh>,<offlow>,<offhigh> […] Note that this is again five bytes, so it can be fixed up "in place". Now the problem is that an interrupt requires two bytes, while a CALL FAR instruction only needs one. As a result, the 32-bit vector (<seg><ofs>) must be compressed into 24 bits. […] This is achieved by two things: First, the <seg> address is encoded as a "handle" to the segment, whose lowest nibble is always zero. This saves four bits. In addition […] the remaining four bits go into the low nibble of the interrupt vector, thus creating anything from INT 80h to 8Fh. […] The interrupt handler for all those vectors is the same. It will "unpack" the address from the three-and-a-half byte notation, look up the absolute address of the segment, and forward the call, after having done its virtual memory loading thing... Return from the call will also pass through the corresponding unlocking code. […] The low nibble of the interrupt vector (80h–8Fh) holds bit 4 through 7 of the segment handle. Bit 0 to 3 of a segment handle are (by definition of a Geos handle) always 0. […] all Geos API run through the "overlay" scheme […]: when a Geos application is loaded into memory, the loader will automatically replace calls to functions in the system libraries by the corresponding INT-based calls. Anyway, these are not constant, but depend on the handle assigned to the library's code segment. […] Geos was originally intended to be converted to protected mode very early on […], with real mode only being a "legacy option" […] almost every single line of assembly code is ready for it […]
- Paul, Matthias R. (2002-04-11). "Re: [fd-dev] ANNOUNCE: CuteMouse 2.0 alpha 1". freedos-dev. Archived from the original on 2020-02-21. Retrieved 2020-02-21.
[…] in case of such mangled pointers […] many years ago Axel and I were thinking about a way how to use *one* entry point into a driver for multiple interrupt vectors (as this would save us a lot of space for the multiple entry points and the more or less identical startup/exit framing code in all of them), and then switch to the different interrupt handlers internally. For example: 1234h:0000h […] 1233h:0010h […] 1232h:0020h […] 1231h:0030h […] 1230h:0040h […] all point to exactly the same entry point. If you hook INT 21h onto 1234h:0000h and INT 2Fh onto 1233h:0010h, and so on, they would all go through the same "loophole", but you would still be able to distinguish between them and branch into the different handlers internally. Think of a "compressed" entry point into a A20 stub for HMA loading. This works as long as no program starts doing segment:offset magics. […] Contrast this with the opposite approach to have multiple entry points (maybe even supporting IBM's Interrupt Sharing Protocol), which consumes much more memory if you hook many interrupts. […] We came to the result that this would most probably not be save in practise because you never know if other drivers normalize or denormalize pointers, for what reasons ever. […]
(NB. Something similar to "fat pointers" specifically for Intel's real-mode segment:offset addressing on x86 processors, containing both a deliberately denormalized pointer to a shared code entry point and some info to still distinguish the different callers in the shared code. While, in an open system, pointer-normalizing 3rd-party instances (in other drivers or applications) cannot be ruled out completely on public interfaces, the scheme can be used safely on internal interfaces to avoid redundant entry code sequences.) - Bright, Walter (2009-12-22). "C's Biggest Mistake". Digital Mars. Archived from the original on 2022-06-08. Retrieved 2022-07-11. [1]
- Holden, Daniel (2015). "A Fat Pointer Library". Cello: High Level C. Archived from the original on 2022-07-11. Retrieved 2022-07-11.