Friday, May 28, 2010

c++ 동적라이브러리 / 동적 클래스

1 http://wiki.kldp.org/wiki.php/DocbookSgml/C%2B%2B-dlopen#LOADINGCLASSES

C++ dlopen mini HOWTO

IsottonAaron

김경태

dlopen API를 이용하여 동적으로 C++ 함수와 클래스를 적재하는 방법을 설명한다.

고친 과정
고침 1.002002-06-19고친이 AI
copyright와 license부분을 앞쪽으로 옮겼다. 용어에 관한 부분을 추가했다. 약간 변화가 있었다.
고침 0.972002-06-19고친이 JYG
문장과 문법을 약간 가다듬었다.
고침 0.962002-06-12고친이 AI
참고 문헌을 추가했다. 외부 함수와 변수들의 설명을 수정했다.
고침 0.952002-06-11고친이 AI
아주 약간 개선.
고침 0.92002-06-10고친이 AI
초안 작성.

1. 소개

Unix C++ 프로그래머 사이에서 자주 발생하는 질문은 dlopenAPI를 이용하여 C++ 함수와 클래스를 적재하는 방법에 관한 것입니다.

사실 이것은 항상 간단한 것만은 아니기 때문에,약간의 설명이 필요합니다. 이 mini HOWTO에서 그것에 관한 내용을 다루고 있습니다.

이 문서를 이해하기 위해서는 C, C++,그리고dlopen API 에 대해서 어느 정도 알고 있어야 할 것입니다.

이 HOWTO의 원문은 http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/에 있습니다.


1.1. Copyright and License

이 문서 C++ dlopen mini HOWTO의 저작권은 Aaron Isotton 에게 있습니다. 자유 소프트웨어 재단에서 발표한, 1.1 혹은 그 이후 버전의 GNU Free Documentation License의 조항에 따라 이 문서를 복사하거나, 배포 혹은 변경하는 것이 허용됩니다.


1.2. 경고(Disclaimer)

이 문서의 내용으로 인한 책임은 지지 않습니다. 여기에 쓰여진 개념이나 예제 그리고 정보를 사용하여 발생한 문제에 대한 책임은 모두 당신의 책임입니다. 여기에는 당신의 시스템에 피해를 줄 수 있는 오류나 부정확한 것들이 있을수도 있습니다. 주의하여 주시기 바라며, 저는 여기에 어떠한 책임도 지지 않을 것입니다.

모든 저작권은 구체적으로 언급하지 않았다면, 그것들 각각의 소유자가 가지고 있습니다. 이 문서에 있는 용어의 사용이 어느 등록상표나 서비스 마크의 효력에 영향을 끼치는 것으로 간주해서는 안 됩니다. 특별한 제품이나 브랜드를 지명하는 것이 상품등의 추천으로 보여서는 안 됩니다.


1.3. 도와 주신분

이 문서에서, 나는 이 분들께 감사하게 되어 기쁘게 생각합니다.

  • Joy Y Goodreau 씨 는 교정에 도움을 주셨습니다.

  • D. Stimitis 씨 는 formatting 과 name mangling에 대한 몇가지 이슈를 지적해주셨고, extern "C"에 대한 몇가지 난해한 점을 지적해주셨습니다.


1.4. Feedback

이 문서에 대한 반응은 언제나 환영입니다. 당신이 추가했으면 하는 것, 의견, 비판을 다음 주소로 보내주시기 바랍니다 .


1.5. 이 문서에서 쓰인 용어들

dlopen API

dlclose, dlerror, dlopen,그리고 dlsym 함수는 dlopen(3) 매뉴얼 페이지에서 설명하고 있습니다.

이 글에서"dlopen"이라고 썼을 때는, dlopen함수 하나를 지칭하는 것이고, "dlopen API" 라고 썼을 때에는, API 전체를 지칭하는 것입니다.


2. 문제

프로그램이 실행될때, 라이브러리를 적재해야 할 때가 가끔씩 있을 것입니다. 당신이 프로그램에 들어가는 플러그인이나 모듈을 만들고 있을때 이러한 일은 종종 발생합니다.

C언어에서, 동적으로 라이브러리를 적재하는 것은 매우 간단합니다. (dlopen, dlsym 그리고 dlclose를 호출하는 것만으로 충분합니다) C++에서는 약간 더 복잡합니다. C++ 라이브러리를 동적으로 적재하는 것이 어려운 이유중에 일부분은name mangling 때문이고, 일부분은 dlopen API가 C를 염두에 두고 만들어졌기 때문에 class를 적재하는 적당한 방법을 제공하지 못하기 때문입니다.

C++에서 라이브러리를 적재하는 방법에 대해 설명하기 전에, name mangling에 대해서 자세히 살펴보고, 문제를 분석해 봅시다. 비록 당신이 name mangling에 관심이 없더라도, 나는 당신이 그것에 대한 설명을 읽기를 바랍니다. 왜냐하면 그것은 당신이 왜 문제가 발생하고 어떻개 해결해야 하는지를 이해하는데 도움을 주기 때문입니다.


2.1. Name Mangling

모든 C++프로그램(혹은 라이브러리나 Object 파일)에서, 모든 non-static 함수는 이진 파일에서 symbol로 표현됩니다. 이러한 symbol들은 프로그램(혹은 라이브러리나 Object 파일)에서, 유일하게 함수를 확인하는, 특수한 문자열입니다.

C에서는, symbol의 이름은 함수의 이름과 동일합니다. strcpy의 symbol은 strcpy입니다. C에서는 두개의 non-static 함수가 같은 이름을 가질 수 없기 때문에 이것이 가능합니다.

C++은 오버로딩을 허용하고 (같은 이름을 가지지만 인자가 다른 함수를 정의할 수 있다), C가 가지고 있지 않은 여러가지 특징들 — Class, member function, exception specifications — 을 가지고 있기 때문에, 그냥 단순히 함수 이름을 symbol 이름으로 쓸 수는 없습니다. 이 문제를 해결하기 위해서, C++에서는 name mangling(이름 엉망으로 만들기)이라는 것을 사용합니다. 이것은 함수이름과 모든 필요한 정보 모두를(인자의 크기나 갯수와 같은) 컴파일러만이 알아볼 수 있는 이상한 문자열로 바꿔버립니다. 예를 들어 foo라는 함수가 있다면, name mangling에 의해 foo@4%6^로 바뀔 것입니다.

name mangling과 관련하여 생기는 문제점의 하나는 C++ 표준 (현재는 [ISO14882])이 그 방법을 정의해 놓지 않았다는 점입니다. 이것은 모든 컴파일러들마다 자기만의 방법 으로 name mangling을 한다는 것을 의미합니다. 어떤 컴파일러는 name mangling 알고리즘이 버전에 따라 다르기도 합니다. (g++ 2.x 와 3.x에서 뚜렷하게 드러납니다.) 비록 당신이 특정한 컴파일러가 어떻게 이름을 바꾸는지 이해했다고 해도(그래서 dlsym을 통해 그 함수를 적재할 수 있게 된다고 해도), 그것은 아마 그 특정한 컴파일러에서만 효과가 있을 것이고, 다음 버전의 컴파일러에서는 이미 사용할 수 없을지도 모릅니다.


2.2. Classes

dlopen API 의 또다른 문제는, 이들이함수를 적재하는 것만을 지원하고 있다는 점입니다. 하지만 C++에서는 라이브러리가 종종 당신이 프로그램에서 쓰고자 하는 Class를 노출시키고는 합니다. 분명히, 클래스를 사용하기 위해서 당신은 그것의 인스턴스를 만들어야 하지만, 그것은 쉽지 않습니다.


3. 해결 방법

3.1. extern "C"

C++에는 extern "C"라는, C binding으로 함수를 정의하는 특별한 키워드가 있습니다. extern "C"로서 선언된 함수는 C처럼 함수의 이름을 symbol의 이름으로 사용합니다. 이러한 이유로, 멤버함수가 아닌 함수들만이 extern "C"로서 선언될 수 있고, 이러한 함수들은 오버로딩을 할 수 없습니다.

이런 심한 제한이 있지만, extern "C"함수는 C 함수처럼 dlopen을 써서 동적으로 적재할 수 있기 때문에 매우 유용합니다.

이것은 extern "C"로 선언된 함수가 C++ 코드를 포함할 수 없다는 것을 의미하는 것이아닙니다. 이런 함수는 어느 종류의 인자라도 받을 수 있고, C++의 특징을 쓸 수 있습니다.


3.2. 함수를 적재하는 법

C++에서 함수는 dlsym을 통해 C처럼 적재됩니다. 당신이 적재하고자 하는 함수는 symbol의 이름이 엉망으로 되지 않도록(name mangling이 이루어지지 않도록), extern "C"로서의 자격을 갖춰야 할 것입니다.

예 1. 함수를 적재하기

main.cpp:

#include  #include    int main() {     using std::cout;     using std::cerr;      cout << "C++ dlopen demo\n\n";      // open the library     cout << "Opening hello.so...\n";     void* handle = dlopen("./hello.so", RTLD_LAZY);          if (!handle) {         cerr << "Cannot open library: " << hello =" (hello_t)">

hello.cpp:

#include   extern "C" void hello() {     std::cout << "hello" << '\n'; }

hello라는 함수는 hello.cpp에서 extern "C"로 선언되었습니다. 이것은 main.cpp에서 dlsym을 사용하여 적재할 수 있습니다. 함수는 extern "C" 로서의 자격을 갖추어야 합니다. 그렇지 않다면 우리는 hello 함수의 symbol의 이름을 알 수 없을테니까요.

주의

extern "C"의 선언에는 두가지의 다른 형태가 있습니다: 하나는 위에서 썼던extern "C"의 방법이고, 또 다른 하나는 extern "C" { … }형태로 중괄호 사이에 선언이 들어가는 방법입니다. 첫번째(inline)형태는 extern 연결과 C 언어 연결을 갖습니다. 두번째 형태는 C언어 연결에만 영향을 끼칩니다. 따라서 아래의 두 가지 선언 방법은 동일한 것입니다.

extern "C" int foo; extern "C" void bar();             
extern "C" {      extern int foo;      extern void bar();  }

externextern이 아닌 함수의 선언에는 차이가 없기때문에, 당신이 어떠한 변수도 선언하지 않는 한 문제가 없습니다. 당신이 변수를 선언한다면,다음의 두 선언 방법은 같지 않다는 것을 명심해야 합니다.

extern "C" int foo;
extern "C" {     int foo; }

좀 더 자세한 설명을 원하시면, paragraph 7에 주의를 기울여 [ISO14882]의 7.5를 읽어보시거나, [STR2000]의 paragraph 9.2.4.를 참조해주십시오.

외부 변수로 무언가를 하기 전에, see also부분에 적혀 있는 문서들을 정독해주시기 바랍니다.


3.3. 클래스를 적재하는 법

클래스를 적재하는 것은 좀 더 어렵습니다. 왜냐하면 우리는 클래스의인스턴스를 필요로 하지, 함수에 대한 포인터를 필요로 하는게 아니기 때문입니다.

클래스가 실행파일에 정의되어 있지 않은데다가,(몇몇 상황에서는) 클래스의 이름조차도 알 수 없기 때문에, 우리는 new 를 이용하여 클래스의 인스턴스를 생성할 수 없습니다.

이것은 다형성(polymorphism) 을 통해 해결할 수 있습니다. 우리는 기반 클래스, 가상의 멤버를 가지고 있는 인터페이스클래스를 실행파일내에 선언하고, 실제 구현부의 클래스를 모듈 에 선언합니다. 일반적으로 인터페이스 클래스는 추상적입니다. (클래스가 순수가상함수(pure virtual function)을 가지고 있을때 클래스가 추상적이라고 합니다.)

클래스의 동적인 적재는 일반적으로 플러그인 — 명확하게 정의된 인터페이스를 보여주어야 하는 — 에 쓰이기 때문에, 우리는 어쨌거나 인터페이스 클래스와 거기서 파생된 구현부 클래스를 정의해야 합니다.

다음으로,모듈 안에Class factory function이라는 두개의 도움을 주는 함수를 추가로 선언해야 합니다. 이 함수 중 하나는 클래스의 인스턴스를 만들고 그것의 포인터를 반환하는 역할을 하고, 또 다른 하나의 함수는 factory에서 만들어진 함수의 포인터를 받아 그것(클래스의 인스턴스)를 파괴하는 역할을 합니다. 이 두 함수는 extern "C"의 자격을 가지고 있어야 합니다.

클래스를 모듈에서 쓰기 위해서,두개의 factory function을 우리가 hello함수를 적재했던 것처럼dlsym을 사용하여 적재하십시오.그럼 우리는 우리가 원하는 만큼의 인스턴스를 생성할수도 있고, 파괴할 수도 있습니다.

예 2. 클래스를 적재하는 법

여기서 우리는 일반적인다각형을 인터페이스로 하고, 삼각형을 구현부분으로 할 것입니다.

main.cpp:

#include "polygon.hpp" #include  #include   int main() {     using std::cout;     using std::cerr;      // load the triangle library     void* triangle = dlopen("./triangle.so", RTLD_LAZY);     if (!triangle) {         cerr << "Cannot load library: " << create_triangle =" (create_t*)" destroy_triangle =" (destroy_t*)" poly =" create_triangle();">set_side_length(7);         cout << "The area is: " <<>area() << '\n';      // destroy the class     destroy_triangle(poly);      // unload the triangle library     dlclose(triangle); }

polygon.hpp:

#ifndef POLYGON_HPP #define POLYGON_HPP  class polygon { protected:     double side_length_;  public:     polygon()         : side_length_(0) {}      void set_side_length(double side_length) {         side_length_ = side_length;     }      virtual double area() const = 0; };  // the types of the class factories typedef polygon* create_t(); typedef void destroy_t(polygon*);  #endif

triangle.cpp:

#include "polygon.hpp" #include   class triangle : public polygon { public:     virtual double area() const {         return side_length_ * side_length_ * sqrt(3) / 2;     } };   // the class factories  extern "C" polygon* create() {     return new triangle; }  extern "C" void destroy(polygon* p) {     delete p; }

클래스를 적재할때 주의해야 할 점이 몇가지 있습니다:

  • 당신은 인스턴스를 생성하는 함수와 파괴하는 함수를 모두 제공해야 합니다. 또 당신이 인스턴스를 파괴할때에는 실행파일 내에서 delete를 이용해서 지우지 마시고 ,항상 모듈로 넘겨서 주시기 바랍니다. 이는 C++ 에서 newdelete 가 오버로딩 될 수 있기 때문입니다. 이 경우에 서로 맞지 않는 newdelete 가 호출될 수 있고, 그렇게 되면 메모리 누수나 segmentation fault가 일어날 수도 있습니다.이것은 모듈과 실행파일을 링크할때 서로 다른 표준 라이브러리를 사용했을 경우에도 적용됩니다.

  • 인터페이스 클래스의 소멸자는 구현부 클래스에 소멸자가 없는경우를 제외하고는 가상(virtual)이어야 합니다. 그렇지 않으면 그것은 호출되지 않을 것이고, 메모리 누수나 segmentation fault가 일어날 수도 있습니다.


4. See Also


서지사항

ISO14482 ISO/IEC 14482-1998 — The C++ Programming Language. http://webstore.ansi.org/에서 PDF로 이용가능합니다.

STR2000 Stroustrup Bjarne The C++ Programming Language, Special Edition. ISBN 0-201-70073-5. Addison-Wesley.










1 이유

다양한 애플리케이션에 사용가능한 로그분석 프로그램을 만들려고 한다. 이러한 프로그램의 경우 어떤 애플리케이션에서 사용할지 알 수 없기 때문에, 원본소스에는 수정할 필요 없이 로그분석 알고리즘만 적재가능하도록 만들 필요가 있다.

가장좋은 방법은 main 코드와 알고리즘을 분리시키는 것으로, 이것은 라이브러리의 동적적재를 이용해서 달성가능 하다. 동적적재는 라이브러리문서에 언급되어 있다.

즉 알고리즘을 플러그인 형태로 적재하는 기술인데, 여기에 더해서 C++의 클래스를 동적으로 적재시킬 수 있기를 원했다. 그렇다면, 클래스의 추상화를 이용해서, 좀더 일관된 개발자 인터페이스를 제공할 수 있을 것이기 때문이다.

그러므로 다음의 두가지를 달성하는게 주요 목표가 될 것이다.
  1. 로그분석 알고리즘을 플러그인 형태로 적재할 수 있도록 한다.
  2. 클래스를 적재함으로써, 일관된 개발자 인터페이스를 제공한다.

이것은 전술패턴의 구현으로 볼 수 있을 것이다.

2 C++로 작성된 라이브러리사용의 문제

C++에서 클래스의 동적적재 가능성에 대해 생각해 본다.

C에서의 라이브러리의 동적적재는 명료하다. 이에 대한 내용은 라이브러리만들기 문서를 참고한다.

C++에서는 name mangling 때문에 dlopen()을 이용해서 라이브러리를 적재하는데, 어려움이 있다. 애시당초 dlopen()이 C++을 염두에 두지 않고 만들었다는 것도 문제일 것이다.

C와 C++은 함수를 가리키기위한 symbol 테이블을 가지고 있다. 어떤 함수를 호출하면 symbol 테이블을 뒤져서, 이진파일 내에서 함수의 원본의 위치를 알아내어서 읽어들이고 실행하는 것으로 묘사할 수 있다.

C는 symbol이 하나의 함수와 대응한다. 그렇지만 C++은 overloading으로 인하여서, 함수이름과 심볼이 일치하지 않는 경우가 발생한다. 이름은 같지만 인자가 다른 함수가 대표적인 경우다. 그러므로, C++로 공유라이브러리를 만들고자 할경우, 이 함수는 반드시 유일하다는 것을 컴파일러에게 알려줄 필요가 있다.

3 extern "C" 를 이용한 해결

오버로딩으로 인해서 발생하는 문제는 extern "C"를 이용하면 해결할 수 있다. extern "C" 는 해당 함수가 심볼이름과 일치될 것이라는 것을 알려준다. 이를테면 C 함수와 마찬가지로 사용하겠다는 의미다. 대신 C++의 기능인 overload등은 사용할 수 없게 된다.

예컨데 hello란 함수가 있다면, 다음과 같이 extern "C"를 이용해서 정의할 수 있다.
#include     using namespace std;  extern "C" void hello()  {      cout << "hello" << "\n";      }   

4 class의 동적적재

exern "C"를 이용해서, 오버로딩이 필요없는 함수를 적재시키는 법에 대해서 알아봤다. 그렇다면, class는 어떨까.

안타깝게도 class는 dlopen()을 이용해서 호출할 수가 없다. 애초에 dlopen이 class를 염두에 두고 만들어진게 아니기 때문이다. 가장 일반적으로 사용할 수 있는 방법은 class에 대한 포인터를 넘겨주는 factory 함수를 만들고, 이 포인터를 이용해서 메서드를 호출하는 방법일 것이다.

다음은 factory 함수를 이용해서 클래스를 호출하는 예제 프로그램이다. 프로그램이름은 main.cc로 하겠다.
#include   #include   #include   #include   #include   #include "mymodule1.h"    int main(int argc, char **argv)  {    void *handle;      char *error;    Test *LTest;      handle = dlopen("libmymodule.so", RTLD_LAZY);    if (!handle)    {      perror("Open Library\n");      exit(0);    }      // Func_Init    init_t* init_myFunc = (init_t *)dlsym(handle, "Func_Init");    if ((error = dlerror()) != NULL)    {      printf("ERROR : %s\n",error);      exit(0);    }    LTest = init_myFunc();    LTest->Count();    LTest->Count();    LTest->Print();      // Func_destory    destroy_t *destroy_myFunc = (destroy_t *)dlsym(handle, "Func_destory");    destroy_myFunc(LTest);  }   

mymodule1.h로 class Test가 선언되어 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 
#ifndef _MYMODULE_H_  #define _MYMODULE_H_    #include   #include     using namespace std;    class Test  {    private:      int count;    public:      Test();      ~Test()      {        printf("Destory\n");      }      void Count();      void Print();  };  extern "C" class Test* Func_Init();  extern "C" void Func_destory(class Test *aTest);    typedef void destroy_t(Test*);  typedef Test* init_t();  #endif   
Funic_initFunc_destory라는 factory 함수가 선언되어있다.

다음은 Test 클래스와 factory 함수가 정의되어 있는 코드로 파일명은 mymodule1.cc다.
#include "mymodule1.h"    Test::Test()  {      printf("Init\n");      count=0;  }    void Test::Count()  {      count++;  }    void Test::Print()  {      printf("Count is %d\n", count);  }    class Test* Func_Init()  {      Test *myTest;      myTest = new Test;      return myTest;  }    void Func_destory(class Test *aTest)  {      delete aTest;  }    void Func_Test(class Test *aTest)  {      aTest->Count();      aTest->Print();  }   

다음은 컴파일 방법이다. 먼저 mymodule1.cc를 공유라이브러리형태로 컴파일 한다.
# g++ -fPIC -c mymodule1.cc  # g++ -shared -W1,-soname,libmymodule.so.1 -o libmymodule.so.1.0.1 mymodule1.o   

main.cc를 컴파일 한다.
g++ -o main main.c -ldl -lmymodule   
일반적인 동적적재의 경우 함수원형이 필요 없지만 class의 경우에는 메서드를 호출해야 하기 때문에 -l옵션을 이용해서 메서드에 대한 정의가 있는 라이브러리 명시적으로 지시해줘야 한다.

만들어진 프로그램을 실행시켜 보자.
$ ./main  Init  Count is 2  Destory   
제대로 실행됨을 알 수 있다.

5 다형성의 구현

factory 함수를 이용해서 클래스를 로딩할 수 있는 방법을 터득했으니, 이제 로그분석 프로그램 제작에 대한 계획을 세워보기로 하자.

이 로그분석 프로그램은 다음과 같은 사항들을 만족시켜야 한다.
  1. 다양한 로그에 대응할 수 있도록 플러그인형식으로 적재할 수 있어야 한다.
  2. 프로그래머에게 공통의 인터페이스를 제공할 수 있어야 한다.
1번 요구사항은 라이브러리의 동적적재를 이용해서 해결할 수 있다. 즉 설정파일을 만든다음에, 해당 로그파일에 대응되어서 적재할 라이브러리를 명시해주면 된다. 이것에 대한 간단한 예는 라이브러리의 사용에 언급되어 있으니, 응용하는데 문제없을 것이다.

2번 요구사항은 클래스를 적재시키고, 클래스의 메서드를 가상함수화 하는 것으로 달성할 수 있을 것이다. 일종의 Interface 클래스를 만들고, 개발자는 Interface의 메서드를 구현하는 방식으로 필요한 모듈을 개발하는 것이다.

6 예전 방식 구현

예전에도 로그분석 비스무레한 프로그램을 만들었던 적이 있다. 보안로그분석프로그램이였는데, 보안장비로 부터 syslog, snmp 혹은 전용socket으로 부터 보안로그를 받아서 분석하고, 이벤트를 생성하는 프로그램이였다. 기본개념은 DOS 공격검사 프로그램의 제작에 소개된 바가 있다.

기본 시스템/네트워크 구성은 다음과 같다.

dos.png

보안장비는 다양한 종류의 보안소프트웨어가 설치되어 있으며, 로그 또한 전혀표준화 되어 있지 않으며, 나름대로의 정책을 가지고 만들어진다. 만약 새로운 장비가 추가된다면, 분석소프트웨어는 해당 장비의 보안로그를 분석할 수 있는 기능이 추가되어야 한다.

이 프로그램은 다음의 조건을 만족할 수 있어야 했다.
  1. 새로운 보안장비 혹은 새로운 보안 제품이 추가되면, 다음과 같은 이유로 새로운 분석모듈을 개발해야 한다.
    • 보안로그가 표준화 되어 있지 않았기 때문
    • plain text, binary 데이터 혹은 DB로 log를 저장하는 등 방식의 차이
    • snmp, syslog, 전용 socket 등 다양한 통신 방법
  2. 가능한 main 코드는 수정되지 않아야 한다.
  3. 모듈의 개발은 일관성이 담보될 수 있어야 한다.
위의 요건들을 만족시키기 위해서 다음의 방법을 사용했다. 일종의 전술패턴의 응용이라고 볼 수 있을 것이다.
  1. 각 제품에 대응되는 라이브러리를 생성한다. 10개의 제품의 로그를 분석해야 한다면, 10개의 라이브러리가 만들어질 것이다.
  2. {해당장비 => 해당장비의 로그를 분석할 수 있는 알고리즘이 포함된 라이브러리}를 선택하도록 전술을 구사한다.
이 방식은 함수포인터를 이용한 프로토콜 처리에 비슷하게 구현되어 있으니 참고하기 바란다.

이 방식은 만족시켜야할 조건중 1번과 2번은 어느정도 만족시킬 수 있지만, 3번을 만족시키지는 못했다. 코드가 객체지향적이지 않았기 때문에, 즉C로 개발되었기 때문이다. 물론 좀더 노력과 시간을 들였다면, 3번을 만족시킬 수 있는 프로그램의 제작이 가능했겠지만, 그때는 그럴만한 실력을 갖추지 못했다.

7 방법의 개선

그래서 class의 가상함수를 이용해서 개발자 인터페이스를 만들고, 이 인터페이스를 상속받아서 실제 구현을 하도록 하는 방법을 생각했다. 여기에서는 대략적인 개념만 소개하는 정도로 하겠다.

다음과 같이 순수가상함수를 포함하는 Interface 클래스를 만들도록 한다. interface.h로 하겠다.
#ifndef _INTERFACE_H_  #define _INTERFACE_H_    class Log  {    private:      int data;    public:      virtual int Create()=0;      virtual int Anly()=0;      virtual int Read()=0;      virtual int Destroy()=0;      virtual ~LogAnly()      {      }  };    #endif   
  • 이제 개발자는 모듈의 작성시 위의 Log클래스를 상속받고, 각 가상함수를 실구현하면 된다.
    • Create : 객체를 생성한다.
    • Anly : 실제 분석을 한다.
    • Read : 분석된 데이터를 읽어들인다.
    • Destroy : 객체를 파괴한다.
다음은 실구현을 포함한 코드다.
#include   #include     class TestLogAnly : public Log  {    private:    struct _CountData    {      int count1;      int count2;    };    _CountData CData;    Config *Cfg;    public:      TestLogAnly();      int Create(char *);      int Anly();      int Read();      int Destroy();      ~TestLogAnly();  };    using namespace std;  int TestLogAnly::Create()  {    cout << "Create " << href="http://www.joinc.co.kr/modules/moniwiki/wiki.php/manSearch?google=none&name=TestLogAnly::Destroy" style="color: rgb(51, 153, 204); text-decoration: none; ">TestLogAnly::Destroy()  {    cout << "Module Anly Destroy" << href="http://www.joinc.co.kr/modules/moniwiki/wiki.php/manSearch?google=none&name=TestLogAnly::Anly" style="color: rgb(51, 153, 204); text-decoration: none; ">TestLogAnly::Anly()  {    CData.count1 = 100;    CData.count2 = 200;    cout << "Log Anly" << href="http://www.joinc.co.kr/modules/moniwiki/wiki.php/manSearch?google=none&name=TestLogAnly::Read" style="color: rgb(51, 153, 204); text-decoration: none; ">TestLogAnly::Read()  {    cout << "ReadData count 1 : "<< href="http://www.joinc.co.kr/modules/moniwiki/wiki.php/manSearch?google=none&name=memset" style="color: rgb(51, 153, 204); text-decoration: none; ">memset((void *)&CData, 0x00, sizeof(CData));  }    // factory 함수의 선언  extern "C" TestLogAnly *Obj_Create();  extern "C" void Obj_Destroy(TestLogAnly *);    TestLogAnly *Obj_Create()  {          TestLogAnly *rtv;          rtv = new TestLogAnly();          return rtv;  }    void Obj_Destroy(TestLogAnly *aLog)  {          delete aLog;  }   
이것으로 동적적재에도 클래스의 가상화, 추상화, 은닉을 적용할 수 있게 되었다.

No comments: