Pluma는 C++로 플러그인을 관리할 수 있게 해주는 가볍고, 단순한 프레임워크이다. Pluma는 오픈 소스로(zlib/libpng) 사용할 수 있다. (오픈 또는 비공개 소스, 무료 또는 상업용 소프트웨어, 개인, 기관 등)
Pluma의 튜토리얼을 통해서 사용법을 알아본다.
Pluma의 소스코드는 다음에서 다운로드 받을 수 있다.
Plugin 인터페이스 구현
Warrior 인터페이스는 일반적인 C++ 추상 클래스이다. 응용프로그램과 플러그인 간 인터페이스로 사용되며 두 프로젝트 모두에서 액세스 할 수 있어야한다.
Warrior.hpp
class Warrior{
public:
virtual std::string getDescription() const = 0;
};
응용프로그램은 WarriorProvider를 통해 Warrior를 만들 수 있다. 따라서 응용프로그램이 Warrior 를 만들 수있는 WarriorProvider 인터페이스가 필요하다.
이를 위해 Pluma는 두 개의 매크로를 제공한다. 하나의 매크로는 클래스 헤더부분을 생성하고 다른 하나는 코드를 생성한다. 그래서 Pluma.hpp 를 포함해야한다.
Warrior.hpp에서 WarriorProvider 클래스 선언을 생성.
PLUMA_PROVIDER_HEADER(Warrior);
Warrior.cpp에는 Provider 소스 만 포함된다.
#include "Warrior.hpp"
PLUMA_PROVIDER_SOURCE(Warrior, 1, 1);
PLUMA_PROVIDER_SOURCE 매크로의 첫 번째 인수는 제공하려는 객체 유형이다. 두 번째 인수는 인터페이스의 현재 버전이고 마지막 인수는 호환되는 가장 낮은 버전을 나타낸다.
Plug-in side 구현
플러그인으로 Eagle과 Jaguar 기능을 추가하고 싶다.
먼저 플러그인 인터페이스헤더(Warrior)를 추가하고 컴파일러에게 Warrior 헤더의 위치를 알려주어야한다.
Eagle 클래스는 Warrior를 서브클래싱한다.
class Eagle: public Warrior{
public:
std::string getDescription(){
return "Eagle Warrior: soldier of the sun";
}
};
플러그인은 Eagle 에 대한 EagleProvider 클래스를 선언해야한다. Pluma는 다음의 매크로를 제공한다.
PLUMA_INHERIT_PROVIDER(Type, BaseType)
Type 이 BaseType 에서 상속되고 BaseTypeProvider 클래스가 존재하는 경우 (즉, BaseType 은 공유 인터페이스 임) 이 예제에서는 다음과 같이 선언한다.
PLUMA_INHERIT_PROVIDER(Eagle, Warrior);
이 매크로를 사용하려면 Pluma 메인 헤더를 포함해야 한다.
#include <Pluma/Pluma.hpp>
Connection 함수 정의
플러그인은 connect 함수를 통해 응용 프로그램에 연결할 수 있다.
connect 함수는 PLUMA_CONNECTOR 매크로를 사용하여 선언되는데 Windows 플랫폼에서 __declspec(dllexport) 지시문을 사용하기위함이다.
사실 이 매크로는 다른 플랫폼에서는 필요하지 않지만 이식성을 높이기 위해 사용하는 것이 좋다.
#include <Pluma/Connector.hpp>
PLUMA_CONNECTOR
bool connect(pluma::Host& host){
...
}
Provider 가 제공되면 오너쉽은 응용프로그램이 갖게 된다.
host.add( new EagleProvider() );
host.add( new JaguarProvider() );
Application side 구현
우선 Pluma 헤더가 포함되어야 한다. 그리고 Pluma 유형의 객체를 생성한다.
WarriorProvider 유형의 Provider 로 설정한다.
manager.acceptProviderType< WarriorProvider >();
이제 플러그인을로드하기 위해 load 함수를 사용한다.
manager.load("plugins", "elite-warriors");
첫 번째 인수는 플러그인을 검색 할 폴더이다. 두 번째 인수는 확장명이 없는 플러그인 파일이름이다.
폴더에서 모든 플러그인을 로드 하려면 다음과 같이 호출한다.
pluma.loadFromFolder("plugins", true);
두 번째 인수는 선택 사항이고 true이면 하위 폴더에서도 라이브러리를 찾아 로드한다.
이제 Provider 에 액세스 할 수 있다. WarriorProvider 포인터를 벡터에 넣는다.
std::vector<WarriorProvider*> providers;
manager.getProviders(providers);
마침내 Provider 를 사용하여 Warrior 를 생성할 수 있다. 이 예제에서는 각 객체를 생성하고 getDescription 을 호출했다.
std::vector<WarriorProvider*>::iterator it;
for (it = providers.begin() ; it != providers.end() ; ++it){
Warrior* warrior = (*it)->create();
std::cout << warrior->getDescription() << std::endl;
delete warrior;
}
create 함수에는 인수가 없다. 플러그인 클래스는 기본 생성자 만 제공해야 하고 인수가 있는 생성자는 사용할 수 없다.
일부 데이터를 사용하여 객체를 초기화해야 하는 경우 공유 인터페이스에서 초기화 함수를 정의하여 필요한 인수를 전달해야한다.
범위가 끝나면 자동으로 플러그인은 언로드된다.
▲Windows에서 MinGW 7.3.0 컴파일 후 실행. Plugin.dll (플러그인)을 로드하여 각각 Description을 출력한다.