লিনাক্সে সি ++ ডায়নামিক শেয়ার্ড লাইব্রেরি


167

এটি g ++ সহ ডায়নামিক শেয়ার্ড লাইব্রেরি সংকলনের ফলোআপ ।

আমি লিনাক্সে সি ++ তে একটি ভাগ করা শ্রেণীর পাঠাগার তৈরি করার চেষ্টা করছি। আমি গ্রন্থাগারটি সংকলন করতে সক্ষম হয়েছি এবং আমি এখানে এবং এখানে যে টিউটোরিয়ালগুলি পেয়েছি তা ব্যবহার করে কিছু (অ-শ্রেণি) ফাংশন কল করতে পারি । আমার সমস্যাগুলি শুরু হয় যখন আমি গ্রন্থাগারে সংজ্ঞায়িত ক্লাসগুলি ব্যবহার করার চেষ্টা করি। আমি যে দ্বিতীয় টিউটোরিয়ালটির সাথে লিঙ্ক করেছি তাতে লাইব্রেরিতে সংজ্ঞায়িত ক্লাসগুলির অবজেক্ট তৈরির জন্য চিহ্নগুলি কীভাবে লোড করা যায় তা দেখানো হয়, তবে কোনও কাজ শেষ করতে এই বিষয়গুলি ব্যবহার করা থেকে বিরত থাকে ।

ভাগ করে নেওয়া সি ++ শ্রেণির লাইব্রেরি তৈরির জন্য আরও একটি সম্পূর্ণ টিউটোরিয়াল সম্পর্কে কেউ কি জানেন যা এটি দেখায় যে কীভাবে পৃথক এক্সিকিউটেবলে এই শ্রেণিগুলি ব্যবহার করতে হয় ? একটি খুব সহজ টিউটোরিয়াল যা অবজেক্ট তৈরি, ব্যবহার (সহজ গিটার এবং সেটটারগুলি ভাল হবে) দেখায় এবং মুছে ফেলা চমত্কার হবে। একটি লিঙ্ক বা কিছু ওপেন সোর্স কোডের একটি রেফারেন্স যা ভাগ করা শ্রেণীর লাইব্রেরির ব্যবহারকে চিত্রিত করে তা সমানভাবে ভাল হবে।


যদিও কোডেলজিক এবং নিম্রডমের উত্তরগুলি কাজ করে, আমি কেবল এটি যুক্ত করতে চেয়েছিলাম যে আমি এই প্রশ্নটি জিজ্ঞাসা করার পর থেকে শুরু করে লিনাক্স প্রোগ্রামিংয়ের একটি অনুলিপি তুলেছি এবং এর প্রথম অধ্যায়ে সি কোড এবং স্থির এবং ভাগ উভয় লাইব্রেরি তৈরি এবং ব্যবহারের জন্য ভাল ব্যাখ্যা রয়েছে । এই উদাহরণগুলি সেই বইয়ের পুরানো সংস্করণে গুগল বুক অনুসন্ধানের মাধ্যমে পাওয়া যায় ।


আমি নিশ্চিত না যে এটি ব্যবহার করে আপনি কী বোঝাতে চেয়েছেন তা আমি বুঝতে পেরেছি, একবার যখন বস্তুর পয়েন্টারটি ফিরে আসে, আপনি কোনও বস্তুর সাথে অন্য কোনও পয়েন্টার ব্যবহার করার মতো ব্যবহার করতে পারেন।
কোডেলোগিক

আমি যে নিবন্ধটির সাথে লিঙ্ক করেছি সেটিতে dlsym ব্যবহার করে কীভাবে কোনও অবজেক্ট ফ্যাক্টরি ফাংশনে ফাংশন পয়েন্টার তৈরি করা যায় তা দেখানো হয়েছে। এটি লাইব্রেরি থেকে অবজেক্ট তৈরি এবং ব্যবহারের জন্য সিনট্যাক্সটি দেখায় না।
বিল

1
আপনার ক্লাস বর্ণনা করে শিরোনাম ফাইল প্রয়োজন হবে। লোডের সময় আপনাকে ওএসকে লাইব্রেরিটি সন্ধান করতে এবং লিঙ্ক করার পরিবর্তে "ডিএলএসআইএম" ব্যবহার করতে হবে বলে আপনি কী ভাবেন? আপনার যদি একটি সাধারণ উদাহরণ প্রয়োজন হয় তা আমাকে জানান।
নিমর্মড

3
@ নিম্রডম: "ডিএলএসআইএম" ব্যবহারের বিকল্প কী? আমি 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 );
}

ম্যাক ওএস এক্সে, এর সাথে সংকলন করুন:

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

এটি যদি কোনও প্লাগইন সিস্টেমের জন্য হয় তবে আপনি মাই ক্লাসটি বেস শ্রেণি হিসাবে ব্যবহার করবেন এবং প্রয়োজনীয় সমস্ত ফাংশন ভার্চুয়ালকে সংজ্ঞায়িত করবেন। প্লাগইন লেখক তখন মাই ক্লাস থেকে উদ্ভূত হবে, ভার্চুয়ালগুলিকে ওভাররাইড করবে এবং বাস্তবায়ন করবে create_objectএবং destroy_object। আপনার মূল অ্যাপ্লিকেশনটি কোনওভাবেই পরিবর্তন করার দরকার নেই।


6
আমি এটি চেষ্টা করার প্রক্রিয়াধীন, তবে কেবল একটি প্রশ্ন আছে। অকার্যকর * ব্যবহার করা কি কঠোরভাবে প্রয়োজন, বা এর পরিবর্তে ক্রিয়েট_জেক্ট ফাংশনটি মাই ক্লাস * ফিরিয়ে দিতে পারে? আমি আপনাকে আমার জন্য এটি পরিবর্তন করতে বলছি না, আমি একে অপরকে ব্যবহার করার কোনও কারণ আছে কিনা তা জানতে চাই।
বিল করুন

1
ধন্যবাদ, আমি এটি চেষ্টা করেছি এবং এটি কমান্ড লাইন থেকে লিনাক্সের মতো কাজ করেছিল (একবার আপনি কোড মন্তব্যে প্রস্তাবিত পরিবর্তনটি করেছিলেন)। আমি আপনার সময় প্রশংসা করি।
বিল

1
বাহ্যিক "সি" দিয়ে আপনি এটিকে ঘোষণা করার কোনও কারণ আছে কি? যেহেতু এটি একটি g ++ সংকলক ব্যবহার করে সংকলিত হয়েছে। আপনি সি নামকরণের কনভেনশনটি কেন ব্যবহার করতে চান? সি সি ++ কল করতে পারে না। সি ++ এ লিখিত একটি র‍্যাপার ইন্টারফেস হ'ল সি থেকে এই কল করার একমাত্র উপায়।
ant2009

6
@ ant2009 আপনার প্রয়োজন extern "C"কারণ dlsymফাংশনটি একটি সি ফাংশন। এবং create_objectক্রিয়াকলাপটি ফাংশনটি লোড করতে , এটি সি-স্টাইলের লিঙ্কেজ ব্যবহার করবে। আপনি যদি এটি ব্যবহার না করেন extern "C", create_objectসি ++ সংকলকটিতে নাম-ম্যাংলিংয়ের কারণে .so ফাইলটিতে ফাংশনটির নাম জানার কোনও উপায় থাকবে না ।
কোকক্স

1
দুর্দান্ত পদ্ধতি, এটি মাইক্রোসফ্ট সংকলকটিতে কেউ কী করবে তার সাথে খুব মিল। # অন্য কিছু কাজের সাথে, আপনি একটি দুর্দান্ত প্ল্যাটফর্ম স্বাধীন ব্যবস্থা পেতে পারেন
11

52

নিম্নলিখিতটি ভাগ করা শ্রেণিবদ্ধ লাইব্রেরির একটি উদাহরণ দেখায় [[এইচ, সিপিপি] এবং লাইব্রেরিটি ব্যবহার করে একটি মেইন সিপিপি মডিউল। এটি একটি খুব সাধারণ উদাহরণ এবং মেকফিলটি আরও ভাল করা যেতে পারে। তবে এটি কাজ করে এবং আপনাকে সহায়তা করতে পারে:

shared.h ক্লাস সংজ্ঞায়িত করে:

class myclass {
   int myx;

  public:

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

শেয়ারড সি পি পি গেটেক্স / সেটেক্স ফাংশন সংজ্ঞায়িত করে:

#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;
}

এবং মেকফিল যা libshared.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

প্রকৃত 'মেইন' চালানোর জন্য এবং লিবিশ্রেডের সাথে লিঙ্ক করার জন্য আপনাকে সম্ভবত বোঝার পথটি নির্দিষ্ট করতে হবে (বা এটি / usr / স্থানীয় / lib বা অনুরূপ রাখুন)।

নিম্নলিখিতটি বর্তমান ডিরেক্টরিটিকে লাইব্রেরিগুলির জন্য অনুসন্ধানের পথ হিসাবে চিহ্নিত করে এবং প্রধান (ব্যাশ সিনট্যাক্স) চালায়:

export LD_LIBRARY_PATH=.
./main

প্রোগ্রামটি libshared এর সাথে যুক্ত রয়েছে তা দেখতে আপনি 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
এটি (আমার খুব প্রশিক্ষণহীন চোখের কাছে) স্ট্র্যাটিলিভাবে ল্যাবশেয়ারডকে লিঙ্কযুক্ত হতে দেখা যাচ্ছে run রান-টাইমে ডায়নামিক লিঙ্কিংয়ের পরিবর্তে আপনার এক্সিকিউটেবলের সাথে। আমি কি সঠিক?
বিল

10
না এটি স্ট্যান্ডার্ড ইউনিক্স (লিনাক্স) ডায়নামিক লিঙ্কিং। একটি ডায়নামিক লাইব্রেরিতে ".so" (শেয়ারড অবজেক্ট) এক্সটেনশন থাকে এবং লোড সময় এক্সিকিউটেবলের (এই ক্ষেত্রে প্রধান) সাথে সংযুক্ত থাকে - প্রতিবার মুখ্য লোড হয়। স্থির লিঙ্কিং লিঙ্ক সময়ে ঘটে এবং এক্সটেনশন ".এ" (সংরক্ষণাগার) সহ গ্রন্থাগারগুলি ব্যবহার করে।
নিমরোডম

9
এটি গতিশীলভাবে নির্মাণের সময় সংযুক্ত করা হয় । অন্য কথায় আপনি যে লাইব্রেরির সাথে সংযোগ দিচ্ছেন তার পূর্ব জ্ঞান আপনার প্রয়োজন (উদাহরণস্বরূপ dlopen এর জন্য 'dl' এর সাথে যুক্ত)। এটি কোনও ব্যবহারকারী নির্দিষ্ট ফাইলের নাম বলার উপর ভিত্তি করে লাইব্রেরি লোড করা থেকে পৃথক , যেখানে পূর্বের জ্ঞানের প্রয়োজন হয় না।
কোডোলেজিক

10
আমি যা (খারাপভাবে) ব্যাখ্যা করার চেষ্টা করছিলাম তা হ'ল এই ক্ষেত্রে আপনার বিল্ডিংয়ের সময় লাইব্রেরির নাম জানতে হবে (আপনাকে জিসি-তে ভাগ করে নিতে হবে)। সাধারণত, যখন তথ্যটি উপলব্ধ না হয় তখন কেউ ড্লোপেন () ব্যবহার করে, যেমন গ্রন্থাগারের নাম রানটাইম (যেমন: প্লাগইন গণনা) এ সন্ধান করা হয়।
কোডেলজিক

3
-L. -lshared -Wl,-rpath=$$(ORIGIN)লিঙ্ক করার সময় এটি ব্যবহার করুন এবং তা ফেলে দিন LD_LIBRARY_PATH=.
ম্যাক্সিম এগারুশকিন

9

মূলত, আপনি ভাগ করা লাইব্রেরিতে ক্লাসটি ব্যবহার করতে চান এমন কোডে আপনার ক্লাসের শিরোলেখ ফাইলটি অন্তর্ভুক্ত করা উচিত। তারপরে, আপনি যখন লিঙ্ক করবেন, তখন আপনার কোডটি ভাগ করা লাইব্রেরির সাথে লিঙ্ক করতে '-l' পতাকাটি ব্যবহার করুন । অবশ্যই, এটি ওএস যেখানে সন্ধান করতে পারে সেখানে থাকার জন্য প্রয়োজন। 3.5 দেখুন । একটি ভাগ করা লাইব্রেরি ইনস্টল করা এবং ব্যবহার করা

সংকলনের সময় আপনি কোন লাইব্রেরিটি ব্যবহার করতে চান তা কখনই dlsym ব্যবহারের জন্য নয়। এটি এখানে কেস বলে মনে হচ্ছে না। হতে পারে বিভ্রান্তিটি হ'ল উইন্ডোজ গতিশীল লোড লাইব্রেরিগুলিকে কল করে আপনি সংকলন বা রান-টাইমে লিঙ্কিং করেন কিনা (অ্যানালগাস পদ্ধতি সহ)? যদি তা হয় তবে আপনি ডিএসএসআইএমকে লোডলিবারির সমতুল্য হিসাবে ভাবতে পারেন।

আপনার যদি সত্যই লাইব্রেরিগুলি (যেমন, তারা প্লাগইনগুলি) গতিশীলভাবে লোড করার দরকার পড়ে তবে এই FAQ এ সহায়তা করা উচিত।


1
আমার ডায়নামিক শেয়ার্ড লাইব্রেরি লাগার কারণ হ'ল আমিও পার্ল কোড থেকে এটিকে কল করব। এটি আমার নিজের পক্ষ থেকে একটি সম্পূর্ণ ভুল ধারণা হতে পারে যে আমি অন্যান্য সি ++ প্রোগ্রামগুলিও গতিশীলভাবে কল করতে চাই যা আমি বিকাশ করছি।
বিল

আমি কখনই ইন্টিগ্রেটেড পার্ল এবং সি ++ চেষ্টা করে দেখিনি, তবে আমার মনে হয় আপনার এক্সএস ব্যবহার করা দরকার: johnkeiser.com/perl-xs-c++.html
ম্যাট লুইস

5

পূর্ববর্তী উত্তরের উপরে, আমি হ্যান্ডলার ধ্বংস সম্পর্কে সুরক্ষিত থাকার জন্য আপনাকে আরএআইআই (রিসোর্স অ্যাকুইজিশন ইজ ইনিশিয়ালাইজেশন) প্রতিমাটি ব্যবহার করা উচিত সে সম্পর্কে সচেতনতা বাড়াতে চাই ।

এখানে একটি সম্পূর্ণ কাজের উদাহরণ:

ইন্টারফেস ঘোষণা 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ওভারলোড হ্যান্ডেল করতে চান সেই ক্ষেত্রে আমি তা উপেক্ষা করেছি ।

আরও বিশদ পেতে দুটি পরিষ্কার নিবন্ধ:


এটি একটি দুর্দান্ত উদাহরণ। আরআইআইআই অবশ্যই যাওয়ার উপায়।
ডেভিড স্টেইনহাউয়ার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.