플러그인 프레임워크 직접 쓰기 (2)

5712 단어
플러그인 프로그래밍 인터페이스
플러그인이란 사실 인터페이스를 바탕으로 한 디자인이다.플러그인 기반 시스템의 가장 기본적인 점은 알 수 없는 플러그인을 불러올 수 있는 중심 시스템이 있어야 하고 미리 정의된 인터페이스와 프로토콜을 사용하여 플러그인과 상호작용을 할 수 있다는 것이다.
가장 기본적인 방법은 인터페이스를 정의하여 일련의 플러그인(동적 또는 정적)이 노출해야 할 함수를 제공하는 것이다.이런 실현은 기술적으로는 가능하다고 말하지만, 실제로는 그렇게 간단하게 조작하지는 않는다.한 플러그인은 두 종류의 인터페이스를 지원해야 하지만 한 인터페이스의 함수 집합만 드러날 수 있기 때문이다.이것은 두 종류의 인터페이스가 반드시 함께 혼합되어야 한다는 것을 의미한다.
첫 번째 인터페이스 (프로토콜) 는 일반 플러그인 인터페이스입니다.이 인터페이스는 센터 시스템에서 플러그인을 초기화할 수 있도록 합니다. 플러그인이 제공하는 창설, 소각 대상을 위한 함수를 센터 시스템에 등록할 수 있습니다.이 유니버설 플러그인 인터페이스는 특정 영역과 관련이 없기 때문에 복용 라이브러리로 사용할 수 있다.두 번째 인터페이스는 플러그인 대상이 제공하는 기능 인터페이스다.이 인터페이스는 특정 분야와 관련되어 꼼꼼하게 설계되고 플러그인에 의해 실제적으로 실현되어야 한다.센터 시스템은 이 인터페이스를 이용하여 플러그인 대상과 상호작용을 해야 한다.
다음은 일반적인 플러그인 인터페이스의 헤더 파일을 보여 줍니다.여기서 우리는 세부적인 것을 깊이 연구하지 않고 단지 상대적으로 직관적인 인식을 가지기 위해서이다.
#ifndef PF_PLUGIN_H
#define PF_PLUGIN_H
 
#include <apr-1/apr_general.h>
 
#ifdef __cplusplus
extern "C" {
#endif
 
typedef enum PF_ProgrammingLanguage
{
    PF_ProgrammingLanguage_C,
    PF_ProgrammingLanguage_CPP,
}   PF_ProgrammingLanguage;
 
struct PF_PlatformServices_;
 
typedef struct PF_ObjectParams
{
    const apr_byte_t * objectType;
    const struct PF_PlatformServices_ * platformServices;
} PF_ObjectParams;
 
typedef struct PF_PluginAPI_Version
{
    apr_int32_t major;
    apr_int32_t minor;
} PF_PluginAPI_Version;
 
typedef void * (*PF_CreateFunc)(PF_ObjectParams *);
 
typedef apr_int32_t (*PF_DestroyFunc)(void *);
 
typedef struct PF_RegisterParams
{
    PF_PluginAPI_Version version;
    PF_CreateFunc createFunc;
    PF_DestroyFunc destroyFunc;
    PF_ProgrammingLanguage programmingLanguage;
} PF_RegisterParams;
 
typedef apr_int32_t (*PF_RegisterFunc)(const apr_byte_t * nodeType,
                                       const PF_RegisterParams * params);
 
typedef apr_int32_t (*PF_InvokeServiceFunc)(const apr_byte_t * serviceName,
                                            void * serviceParams);
 
typedef struct PF_PlatformServices
{
    PF_PluginAPI_Version version;
    PF_RegisterFunc registerObject;
    PF_InvokeServiceFunc invokeService;
} PF_PlatformServices;
 
typedef apr_int32_t (*PF_ExitFunc)();
 
typedef PF_ExitFunc (*PF_InitFunc)(const PF_PlatformServices *);
 
#ifndef PLUGIN_API
  #ifdef WIN32
    #define PLUGIN_API __declspec(dllimport)
  #else
    #define PLUGIN_API
  #endif
#endif
 
extern
#ifdef  __cplusplus
"C"
#endif
PLUGIN_API PF_ExitFunc PF_initPlugin(const PF_PlatformServices * params);
 
#ifdef  __cplusplus
}
#endif
 
#endif /* PF_PLUGIN_H */

네가 깨달아야 할 첫 번째 일은 이것이 C 헤더 파일이라는 것이다.이것은 우리의 플러그인 프레임워크를 순수 C 시스템에서 컴파일하고 사용할 수 있으며, 순수 C 플러그인을 작성할 수 있도록 합니다.그러나 이렇게 하면 반드시 C를 사용해야 한다고 한정하지는 않는다. 실제로는 C++를 더 자주 사용하도록 설계되었다.
PF_ProgrammingLanguage 매거진은 플러그인 관리자에게 플러그인 자체가 C++인지 알려 줍니다.
PF_ObjectParams는 플러그인 객체를 만들 때 전달되는 추상적인 구조입니다.
PF_PluginAPI_Version 은 버전 정보를 나타냅니다.이것은 플러그인 관리자가 호환되는 버전의 플러그인만 불러오는 데 도움이 됩니다.
함수 포인터 PFCreateFunc 및 PFDestroyFunc은 플러그인 관리자가 플러그인 대상을 만들고 제거하는 데 사용할 플러그인으로 이루어져야 합니다.
PF_RegisterParams 구조에는 플러그인 관리자가 플러그인 (버전, 생성, 삭제 함수, 개발 언어) 을 초기화할 수 있도록 플러그인이 플러그인 관리자에게 제공해야 하는 모든 정보가 포함되어 있습니다.
PF_플러그인 관리자에 의해 실행되는 RegisterFunc 함수 포인터는 각 플러그인에서 지원하는 객체 유형을 PF 로 허용합니다.플러그인 관리자에 RegisterParams 구조로 등록됩니다.플러그인이 서로 다른 버전의 대상을 등록하고 여러 개의 대상 유형을 등록할 수 있도록 합니다.
PF_InvokeService 함수 포인터는 플러그인이 주 시스템에서 제공하는 로그, 이벤트 처리, 오류 보고 등 다양한 서비스를 호출할 수 있는 일반적인 함수입니다.이 함수는 서비스 이름과 매개 변수 구조를 가리키는 불투명한 바늘을 요구합니다.플러그인은 사용 가능한 서비스와 그것을 어떻게 호출하는지 알아야 한다. (또는 서비스 발견 메커니즘을 실현해야 한다.)
PF_Platform Services 구조는 플랫폼에서 제공하는 모든 서비스(버전, 등록된 객체 및 호출 함수)를 나타냅니다.이 구조는 플러그인을 초기화할 때 모든 플러그인에 전달됩니다.
PF_ExitFunc는 플러그인 종료 함수에 대한 포인터이며 플러그인에 의해 실행됩니다.
PF_InitFunc은 플러그인을 초기화하는 함수 포인터입니다.
PF_initPlugin은 동적 플러그인 (즉 동적 링크 라이브러리나 공유 라이브러리를 통해 배치된 플러그인) 을 초기화하는 함수의 실제 설명입니다.이것은 동적 플러그인에 노출되어 플러그인 관리자가 플러그인을 불러올 때 호출할 수 있습니다.그것은 PF 를 가리킨다Platform 서비스 구조의 바늘이기 때문에 플러그인을 초기화할 때 이 서비스들은 모두 호출할 수 있습니다. (이것이 바로 등록 대상의 이상적인 시기입니다.) 함수는 종료 함수의 바늘을 되돌려줍니다.
정적 플러그인 (정적 링크 라이브러리로 이루어지고 주 프로그램과 직접 연결된 플러그인) 은 init 함수를 실현해야 하지만 PF 라고 명명할 수 없습니다.initPlugin.정적 플러그인이 여러 개 있으면 같은 이름의 함수를 사용할 수 없기 때문이다.
정적 플러그 인의 초기화 과정은 다릅니다.PF 를 통해 마스터 프로그램에서 명시적으로 초기화해야 합니다.InitFunc에서 초기화 함수를 호출합니다.정적 플러그인을 추가하거나 삭제하려면 주 응용 프로그램의 코드를 수정해야 하고, 서로 다른 이름의 init 함수를 찾을 수 있어야 하기 때문에, 이것은 실제적으로 좋지 않은 디자인이다.
자동 등록이라는 기술이 이 문제를 해결하려고 시도했다.자동 등록은 정적 라이브러리의 전역 대상에 의해 이루어진다.이 대상은main () 함수가 실행되기 전에 구성됩니다.이 전역 대상은 플러그인 관리자에게 정적 플러그인 (플러그인 init () 함수를 전달하는 지침을 통해 완성할 수 있습니다.불행하게도, 일부 버전의 Visual C++에서는 이러한 기술이 지원되지 않습니다.
플러그인 작성
플러그인은 어떻게 작성합니까?우리의 플러그인 프레임워크는 가장 일반적인 기능을 제공하기 때문에 현재 조건에서는 메인 응용 프로그램과 상호작용할 수 있는 플러그인을 추가하기 어렵다.따라서 플러그인 프레임워크의 기초 위에서 자신의 응용 프로그램 대상을 다시 구축해야 한다.이것은 응용 프로그램 (플러그인을 불러오는 것) 은 플러그인 자체와 함께 같은 상호작용 모델을 준수해야 한다는 것을 의미한다.일반적으로 이것은 특정 API를 노출하기 위해 플러그인이 특정 유형의 대상을 제공해야 한다는 것을 의미한다.플러그인 프레임워크는 플러그인의 등록, 매거, 불러오는 데 필요한 모든 공공 기초 코드를 제공합니다.
다음 예는 C++ 인터페이스에 정의된 Iactor입니다.이 인터페이스에는 getInitialInfo()와 play()가 있습니다.getInitialInfo () 함수는ActorInfo 구조를 가리키는 바늘이 필요하고play () 는 다른 인터페이스인 Iturn의 바늘이 필요하기 때문에 이 인터페이스는 모든 상황에 대처하기에 충분하지 않습니다.이것은 자주 만나는 상황입니다. 당신은 반드시 이렇게 설계하고 특정한 대상 모델을 지정해야 합니다.
struct IActor
{
    virtual ~IActor() {}
    virtual void getInitialInfo(ActorInfo * info) = 0;
    virtual void play( ITurn * turnInfo) = 0;
};

모든 플러그인은 Iactor 인터페이스의 여러 구현을 등록할 수 있습니다.프로그램이 플러그인으로 등록된 대상을 실례화하기로 결정하면 플러그인으로 이루어진 PF 를 호출합니다CreateFunc 함수.플러그인은 응답을 해서 대상을 만들고 프로그램에 되돌려줍니다.함수 반환값은void*이며, 대상의 창설 작업은 일반적인 플러그인 프레임워크의 일부이기 때문에 특정한 Iactor 인터페이스에 대한 정보를 알 수 없습니다.응용 프로그램은 void *를 Iactor *로 변환한 다음 다른 대상처럼 인터페이스를 통해 함수를 호출합니다.응용 프로그램에서 Iactor 객체를 사용한 경우 등록된 PFDestroyFunc 함수, 플러그인이 객체를 제거합니다.왜 허석구 함수가 필요한지 우리는 이후의 토론에서 소개할 것이다.

좋은 웹페이지 즐겨찾기