लिनक्स पर C ++ डायनेमिक साझा लाइब्रेरी


167

यह g ++ के साथ गतिशील साझा लाइब्रेरी संकलन का अनुवर्ती है ।

मैं लिनक्स पर C ++ में एक साझा क्लास लाइब्रेरी बनाने की कोशिश कर रहा हूं। मैं पुस्तकालय को संकलित करने में सक्षम हूं, और मैं यहां और यहां पाए गए ट्यूटोरियल का उपयोग करके कुछ (गैर-वर्ग) कार्यों को कॉल कर सकता हूं । मेरी समस्याएं तब शुरू होती हैं जब मैं लाइब्रेरी में परिभाषित कक्षाओं का उपयोग करने की कोशिश करता हूं। दूसरा ट्यूटोरियल जिसे मैंने दिखाया था कि लाइब्रेरी में परिभाषित कक्षाओं की वस्तुओं को बनाने के लिए प्रतीकों को कैसे लोड किया जाए, लेकिन किसी भी काम को करने के लिए उन वस्तुओं का उपयोग करने से कम हो जाता है।

क्या कोई साझा C ++ क्लास लाइब्रेरी बनाने के लिए अधिक संपूर्ण ट्यूटोरियल के बारे में जानता है जो यह भी दिखाता है कि उन कक्षाओं को एक अलग निष्पादन योग्य में कैसे उपयोग किया जाए ? एक बहुत ही सरल ट्यूटोरियल जो ऑब्जेक्ट निर्माण, उपयोग (सरल गेटर्स और सेटर ठीक होगा) दिखाता है, और विलोपन शानदार होगा। एक लिंक या कुछ ओपन सोर्स कोड का संदर्भ जो एक साझा क्लास लाइब्रेरी के उपयोग को दिखाता है, उतना ही अच्छा होगा।


हालाँकि कोडेलोगिक और निम्रोडम के उत्तर काम करते हैं, मैं बस यह जोड़ना चाहता था कि मैंने यह सवाल पूछने के बाद से शुरुआत लिनक्स प्रोग्रामिंग की एक प्रति उठाई थी , और इसके पहले अध्याय में सी कोड और अच्छी व्याख्याएं हैं जो स्थैतिक और साझा पुस्तकालयों को बनाने और उपयोग करने के लिए हैं। । ये उदाहरण उस पुस्तक के पुराने संस्करण में Google पुस्तक खोज के माध्यम से उपलब्ध हैं ।


मुझे यकीन नहीं है कि मैं समझता हूं कि आप इसका उपयोग "करने" से करते हैं, एक बार जब ऑब्जेक्ट को एक पॉइंटर लौटाया जाता है, तो आप इसका उपयोग कर सकते हैं जैसे कि आप किसी ऑब्जेक्ट के लिए किसी अन्य पॉइंटर का उपयोग करते हैं।
कोडेलॉजिक

लेख मैं दिखाता है कि कैसे dlsym का उपयोग करके किसी ऑब्जेक्ट फ़ैक्टरी फ़ंक्शन के लिए फ़ंक्शन पॉइंटर बनाने के लिए। यह लाइब्रेरी से ऑब्जेक्ट बनाने और उपयोग करने के लिए सिंटैक्स नहीं दिखाता है।
छिपकली

1
आपको क्लास का वर्णन करने वाली हेडर फ़ाइल की आवश्यकता होगी। आपको क्या लगता है कि आपको ओएस को खोजने और लोड समय पर लाइब्रेरी को लिंक करने देने के बजाय "dlsym" का उपयोग करना होगा? मुझे बताएं कि क्या आपको एक सरल उदाहरण की आवश्यकता है।
नीमरोड

3
@nimrodm: "dlsym" का उपयोग करने का विकल्प क्या है? मैं 3 सी + + प्रोग्राम लिख रहा हूं जो सभी साझा पुस्तकालय में परिभाषित कक्षाओं का उपयोग करेंगे। मेरे पास 1 पर्ल स्क्रिप्ट भी है जो इसका उपयोग करेगी, लेकिन यह अगले सप्ताह के लिए एक पूरी अन्य समस्या है।
को छिपकली

जवाबों:


154

myclass.h

#ifndef __MYCLASS_H__
#define __MYCLASS_H__

class MyClass
{
public:
  MyClass();

  /* use virtual otherwise linker will try to perform static linkage */
  virtual void DoSomething();

private:
  int x;
};

#endif

myclass.cc

#include "myclass.h"
#include <iostream>

using namespace std;

extern "C" MyClass* create_object()
{
  return new MyClass;
}

extern "C" void destroy_object( MyClass* object )
{
  delete object;
}

MyClass::MyClass()
{
  x = 20;
}

void MyClass::DoSomething()
{
  cout<<x<<endl;
}

class_user.cc

#include <dlfcn.h>
#include <iostream>
#include "myclass.h"

using namespace std;

int main(int argc, char **argv)
{
  /* on Linux, use "./myclass.so" */
  void* handle = dlopen("myclass.so", RTLD_LAZY);

  MyClass* (*create)();
  void (*destroy)(MyClass*);

  create = (MyClass* (*)())dlsym(handle, "create_object");
  destroy = (void (*)(MyClass*))dlsym(handle, "destroy_object");

  MyClass* myClass = (MyClass*)create();
  myClass->DoSomething();
  destroy( myClass );
}

Mac OS X पर, इसके साथ संकलित करें:

g++ -dynamiclib -flat_namespace myclass.cc -o myclass.so
g++ class_user.cc -o class_user

लिनक्स पर, इसके साथ संकलित करें:

g++ -fPIC -shared myclass.cc -o myclass.so
g++ class_user.cc -ldl -o class_user

यदि यह एक प्लगइन सिस्टम के लिए था, तो आप MyClass को बेस क्लास के रूप में उपयोग करेंगे और सभी आवश्यक कार्यों को आभासी रूप से परिभाषित करेंगे। प्लगइन लेखक तब MyClass से निकलेगा, वर्चुअल को ओवरराइड करेगा और कार्यान्वित करेगा create_objectऔर destroy_object। आपके मुख्य आवेदन को किसी भी तरह से बदलने की आवश्यकता नहीं होगी।


6
मैं यह कोशिश कर रहा हूँ, लेकिन सिर्फ एक सवाल है। क्या यह शून्य * का उपयोग करने के लिए कड़ाई से आवश्यक है, या इसके बजाय create_object फ़ंक्शन रिटर्न MyClass * कर सकता है? मैं आपको मेरे लिए इसे बदलने के लिए नहीं कह रहा हूं, मैं सिर्फ यह जानना चाहूंगा कि क्या कोई एक दूसरे पर इस्तेमाल करने का कारण है।
छिपकली

1
धन्यवाद, मैंने यह कोशिश की और यह कमांड लाइन से लिनक्स पर काम किया (एक बार जब आपने कोड टिप्पणियों में सुझाए गए परिवर्तन को बनाया था)। मैं आपके समय की सराहना करता हूं।
छिपकली

1
क्या कोई कारण है कि आप इन्हें "C" के साथ घोषित करेंगे? जैसा कि यह एक जी ++ संकलक का उपयोग करके संकलित किया गया है। आप सी नामकरण सम्मेलन का उपयोग क्यों करना चाहेंगे? C को C ++ नहीं कह सकते। C ++ में लिखा गया रैपर इंटरफ़ेस, c से इसे कॉल करने का एकमात्र तरीका है।
ant2009

6
@ ant2009 की आपको आवश्यकता है extern "C"क्योंकि dlsymफ़ंक्शन एक सी फ़ंक्शन है। और create_objectफ़ंक्शन को गतिशील रूप से लोड करने के लिए , यह सी-स्टाइल लिंकेज का उपयोग करेगा। यदि आप इसका उपयोग नहीं करते हैं extern "C", create_objectतो। ++ फ़ाइल में फ़ंक्शन का नाम जानने का कोई तरीका नहीं होगा , क्योंकि C ++ कंपाइलर में नाम-मेनलिंग।
कोकक्स 3'13

1
अच्छा तरीका, यह बहुत कुछ एक Microsoft संकलक पर क्या होगा के समान है। # थोड़ा # काम के साथ, आप एक अच्छा प्लेटफ़ॉर्म स्वतंत्र प्रणाली प्राप्त कर सकते हैं
11:11 बजे

52

निम्नलिखित साझा किए गए साझा पुस्तकालय पुस्तकालय का एक उदाहरण दिखाता है। [h, cpp] और पुस्तकालय का उपयोग कर एक main.cpp मॉड्यूल। यह एक बहुत ही सरल उदाहरण है और मेकफाइल को बहुत बेहतर बनाया जा सकता है। लेकिन यह काम करता है और आपकी मदद कर सकता है:

साझा किया जाता है। वह वर्ग को परिभाषित करता है:

class myclass {
   int myx;

  public:

    myclass() { myx=0; }
    void setx(int newx);
    int  getx();
};

share.cpp getx / setx फ़ंक्शंस को परिभाषित करता है:

#include "shared.h"

void myclass::setx(int newx) { myx = newx; }
int  myclass::getx() { return myx; }

main.cpp वर्ग का उपयोग करता है,

#include <iostream>
#include "shared.h"

using namespace std;

int main(int argc, char *argv[])
{
  myclass m;

  cout << m.getx() << endl;
  m.setx(10);
  cout << m.getx() << endl;
}

और मेकफाइल जो libsared.so उत्पन्न करता है और साझा लाइब्रेरी के साथ मुख्य लिंक करता है:

main: libshared.so main.o
    $(CXX) -o main  main.o -L. -lshared

libshared.so: shared.cpp
    $(CXX) -fPIC -c shared.cpp -o shared.o
    $(CXX) -shared  -Wl,-soname,libshared.so -o libshared.so shared.o

clean:
    $rm *.o *.so

'रन' को वास्तविक रूप से चलाने के लिए और libsaring.so के साथ लिंक करने के लिए आपको संभवतः लोड पथ निर्दिष्ट करना होगा (या इसे / usr / स्थानीय / देयता या समान में रखना होगा)।

निम्नलिखित वर्तमान निर्देशिका को पुस्तकालयों के लिए खोज पथ के रूप में निर्दिष्ट करता है और मुख्य (बैश सिंटैक्स) चलाता है:

export LD_LIBRARY_PATH=.
./main

यह देखने के लिए कि प्रोग्राम libsared.so से जुड़ा हुआ है, आप ldd आज़मा सकते हैं:

LD_LIBRARY_PATH=. ldd main

मेरी मशीन पर प्रिंट:

  ~/prj/test/shared$ LD_LIBRARY_PATH=. ldd main
    linux-gate.so.1 =>  (0xb7f88000)
    libshared.so => ./libshared.so (0xb7f85000)
    libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7e74000)
    libm.so.6 => /lib/libm.so.6 (0xb7e4e000)
    libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0xb7e41000)
    libc.so.6 => /lib/libc.so.6 (0xb7cfa000)
    /lib/ld-linux.so.2 (0xb7f89000)

1
ऐसा प्रतीत होता है (मेरी बहुत अप्रशिक्षित आँख के लिए) आपके निष्पादन योग्य के लिए सांख्यिकीय रूप से लिंक करने के लिए, बल्कि रन-टाइम पर डायनेमिक लिंकिंग का उपयोग करने के बजाय। क्या मैं सही हूँ?
छिपकली

10
नहीं। यह मानक यूनिक्स (लिनक्स) गतिशील लिंकिंग है। एक गतिशील पुस्तकालय में ".so" (साझा वस्तु) का विस्तार होता है और लोड समय पर निष्पादन योग्य (इस मामले में मुख्य) के साथ जुड़ा होता है - हर बार मुख्य लोड होता है। स्टेटिक लिंकिंग लिंक समय पर होती है और एक्सटेंशन ".a" (संग्रह) के साथ पुस्तकालयों का उपयोग करती है।
नीमरोड

9
यह गतिशील रूप से बिल्ड समय पर जुड़ा हुआ है । दूसरे शब्दों में, आपको उस लायब्रेरी के पूर्व ज्ञान की आवश्यकता है जिसे आप लिंक कर रहे हैं (जैसे कि dlopen के लिए 'dl' के विरुद्ध लिंक)। यह डायनामिक रूप से लाइब्रेरी लोड करने से अलग है , कहने के आधार पर, एक उपयोगकर्ता निर्दिष्ट फ़ाइल नाम, जहां पूर्व ज्ञान की आवश्यकता नहीं है।
कोडेलॉजिक

10
जो मैं समझाने की कोशिश कर रहा था (बुरी तरह से) वह यह है कि इस मामले में, आपको लाइब्रेरी का नाम बिल्ड टाइम पर पता होना चाहिए (आपको जीसीसी में पास होना आवश्यक है)। आमतौर पर, जब कोई जानकारी उपलब्ध नहीं होती है, तो dlopen () का उपयोग करता है, यानी रनटाइम पर लाइब्रेरी का नाम खोजा जाता है (जैसे: प्लगइन गणन)।
कोडेलॉजिक

3
-L. -lshared -Wl,-rpath=$$(ORIGIN)लिंक करते समय उपयोग करें और छोड़ें LD_LIBRARY_PATH=.
मैक्सिम Egorushkin

9

मूल रूप से, आपको उस कोड में क्लास की हेडर फ़ाइल शामिल करनी चाहिए जहाँ आप साझा लाइब्रेरी में क्लास का उपयोग करना चाहते हैं। फिर, जब आप लिंक करते हैं, तो साझा कोड के साथ अपने कोड को लिंक करने के लिए '-l' ध्वज का उपयोग करें । बेशक, इसके लिए .so की आवश्यकता होती है जहां ओएस इसे पा सकता है। 3.5 देखें । साझा लाइब्रेरी का इंस्टालेशन और उपयोग

Dlsym का उपयोग उस समय के लिए होता है जब आप संकलन समय पर नहीं जानते हैं कि आप किस लाइब्रेरी का उपयोग करना चाहते हैं। ऐसा लगता है जैसे यह यहाँ मामला नहीं है। शायद भ्रम यह है कि विंडोज गतिशील रूप से भरी हुई पुस्तकालयों को कॉल करता है चाहे आप संकलन या रन-टाइम पर (अनुरूप तरीकों से) लिंकिंग करते हैं? यदि ऐसा है, तो आप Dlsym को LoadLibrary के समकक्ष मान सकते हैं।

यदि आपको वास्तव में पुस्तकालयों को गतिशील रूप से लोड करने की आवश्यकता है (यानी, वे प्लग-इन हैं), तो इस FAQ में मदद करनी चाहिए।


1
एक गतिशील साझा पुस्तकालय की आवश्यकता का कारण यह है कि मैं इसे पर्ल कोड से भी कॉल करूंगा। यह मेरे अपने हिस्से पर एक पूरी गलत धारणा हो सकती है कि मुझे इसे अन्य C ++ प्रोग्रामों से गतिशील रूप से कॉल करने की आवश्यकता है जो मैं विकसित कर रहा हूं।
छिपकली

मैंने कभी भी perl और C ++ को एकीकृत करने की कोशिश नहीं की है, लेकिन मुझे लगता है कि आपको XS का उपयोग करने की आवश्यकता है: johnkeiser.com/perl-xs-c+.html
मैट लुईस

5

पिछले उत्तरों के शीर्ष पर, मैं इस तथ्य के बारे में जागरूकता बढ़ाना चाहूंगा कि आपको हैंडलर विनाश के बारे में सुरक्षित रहने के लिए RAII (रिसोर्स एक्विजिशन इनिशियलाइजेशन) मुहावरे का उपयोग करना चाहिए ।

यहाँ एक पूर्ण कार्य उदाहरण है:

इंटरफ़ेस घोषणा Interface.hpp:

class Base {
public:
    virtual ~Base() {}
    virtual void foo() const = 0;
};

using Base_creator_t = Base *(*)();

साझा पुस्तकालय सामग्री:

#include "Interface.hpp"

class Derived: public Base {
public:
    void foo() const override {}
};

extern "C" {
Base * create() {
    return new Derived;
}
}

गतिशील साझा पुस्तकालय हैंडलर Derived_factory.hpp:

#include "Interface.hpp"
#include <dlfcn.h>

class Derived_factory {
public:
    Derived_factory() {
        handler = dlopen("libderived.so", RTLD_NOW);
        if (! handler) {
            throw std::runtime_error(dlerror());
        }
        Reset_dlerror();
        creator = reinterpret_cast<Base_creator_t>(dlsym(handler, "create"));
        Check_dlerror();
    }

    std::unique_ptr<Base> create() const {
        return std::unique_ptr<Base>(creator());
    }

    ~Derived_factory() {
        if (handler) {
            dlclose(handler);
        }
    }

private:
    void * handler = nullptr;
    Base_creator_t creator = nullptr;

    static void Reset_dlerror() {
        dlerror();
    }

    static void Check_dlerror() {
        const char * dlsym_error = dlerror();
        if (dlsym_error) {
            throw std::runtime_error(dlsym_error);
        }
    }
};

क्लाइंट कोड:

#include "Derived_factory.hpp"

{
    Derived_factory factory;
    std::unique_ptr<Base> base = factory.create();
    base->foo();
}

ध्यान दें:

  • मैं संक्षिप्तता के लिए हेडर फ़ाइलों में सब कुछ डाल दिया। वास्तविक जीवन में आपको निश्चित रूप से अपने कोड को फ़ाइलों .hppऔर .cppफ़ाइलों के बीच विभाजित करना चाहिए ।
  • सरल बनाने के लिए, मैंने उस मामले को नजरअंदाज कर दिया, जहां आप new/ deleteअधिभार को संभालना चाहते हैं ।

अधिक विवरण प्राप्त करने के लिए दो स्पष्ट लेख:


यह एक उत्कृष्ट उदाहरण है। आरएआई निश्चित रूप से जाने का रास्ता है।
डेविड स्टाइनहाऊर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.