IT

Linux의 C ++ 동적 공유 라이브러리

lottoking 2020. 6. 2. 21:15
반응형

Linux의 C ++ 동적 공유 라이브러리


이것은 g ++을 사용한 Dynamic Shared Library 컴파일에 대한 후속 조치 입니다.

Linux에서 C ++로 공유 클래스 라이브러리를 만들려고합니다. 라이브러리를 컴파일 할 수 있으며 herehere 에서 찾은 자습서를 사용하여 (비 클래스) 함수 중 일부를 호출 할 수 있습니다 . 라이브러리에 정의 된 클래스를 사용하려고하면 문제가 시작됩니다. 내가 링크 한 두 번째 튜토리얼은 라이브러리에 정의 된 클래스의 객체를 생성하기위한 심볼을로드하는 방법을 보여 주지만 해당 객체를 사용하여 작업을 완료하는 데는 부족 합니다.

누구든지 별도의 실행 파일에서 해당 클래스 사용 하는 방법을 보여주는 공유 C ++ 클래스 라이브러리를 만드는 더 완벽한 자습서를 알고 있습니까? 객체 생성, 사용 (간단한 게터 및 세터가 훌륭함) 및 삭제가 환상적으로 보이는 매우 간단한 자습서입니다. 공유 클래스 라이브러리 사용을 보여주는 오픈 소스 코드에 대한 링크 또는 참조도 동일합니다.


codelogicnimrodm 의 답변이 효과가 있지만 이 질문을 한 이후 Beginning Linux Programming 의 사본을 선택 했으며 첫 번째 장에는 예제 C 코드와 정적 라이브러리와 공유 라이브러리를 만들고 사용하는 데 대한 좋은 설명이 있습니다. . 이 예는 해당 도서 의 이전 버전 에서 Google 도서 검색을 통해 제공됩니다 .


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

Linux에서 다음을 사용하여 컴파일하십시오.

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

이것이 플러그인 시스템 인 경우 MyClass를 기본 클래스로 사용하고 필요한 모든 기능을 가상으로 정의합니다. 하고자 다음 MyClass의에서 파생 플러그인 저자는 virtuals에를 무시하고 구현 create_object하고 destroy_object. 기본 응용 프로그램은 어떤 식 으로든 변경할 필요가 없습니다.


The following shows an example of a shared class library shared.[h,cpp] and a main.cpp module using the library. It's a very simple example and the makefile could be made much better. But it works and may help you:

shared.h defines the class:

class myclass {
   int myx;

  public:

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

shared.cpp defines the getx/setx functions:

#include "shared.h"

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

main.cpp uses the class,

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

and the makefile that generates libshared.so and links main with the shared library:

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

To actual run 'main' and link with libshared.so you will probably need to specify the load path (or put it in /usr/local/lib or similar).

The following specifies the current directory as the search path for libraries and runs main (bash syntax):

export LD_LIBRARY_PATH=.
./main

To see that the program is linked with libshared.so you can try ldd:

LD_LIBRARY_PATH=. ldd main

Prints on my machine:

  ~/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)

Basically, you should include the class' header file in the code where you want to use the class in the shared library. Then, when you link, use the '-l' flag to link your code with the shared library. Of course, this requires the .so to be where the OS can find it. See 3.5. Installing and Using a Shared Library

Using dlsym is for when you don't know at compile time which library you want to use. That doesn't sound like it's the case here. Maybe the confusion is that Windows calls the dynamically loaded libraries whether you do the linking at compile or run-time (with analogous methods)? If so, then you can think of dlsym as the equivalent of LoadLibrary.

If you really do need to dynamically load the libraries (i.e., they're plug-ins), then this FAQ should help.


On top of previous answers, I'd like to raise awareness about the fact that you should use the RAII (Resource Acquisition Is Initialisation) idiom to be safe about handler destruction.

Here is a complete working example:

Interface declaration: Interface.hpp:

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

using Base_creator_t = Base *(*)();

Shared library content:

#include "Interface.hpp"

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

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

Dynamic shared library handler: 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);
        }
    }
};

Client code:

#include "Derived_factory.hpp"

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

Note:

  • I put everything in header files for conciseness. In real life you should of course split your code between .hpp and .cpp files.
  • To simplify, I ignored the case where you want to handle a new/delete overload.

Two clear articles to get more details:

참고URL : https://stackoverflow.com/questions/496664/c-dynamic-shared-library-on-linux

반응형