[완료] AfxGetApp()와 비슷한 함수를 만들려면..?

senah의 이미지

간단한 프레임웍을 만들고 있는데요, 다른건 대충 해결을 했는데 이거 하나가 발목을 잡네요. 그러니까 main()과 같은 진입점을 직접 코딩하지 않고, 쓰는 쪽에서는 특정 클래스를 상속 받아서 여기서 초기화 코드를 써주는 방식으로 하려고 하는데, 이렇게 하려다보니까 MFC의 CWinApp처럼 전역 변수로 선언되며, 또 이것을 상속받는 클래스의 인스턴스 포인터를 알아내야 합니다.

그런데, 전역으로 선언된 변수의, 그것도 쓰는 쪽에서 상속 받는 파생 클래스의 인스턴스 포인터를 얻는 방법이 있나요..? 기본 클래스의 포인터만을 얻는 문제라면 아무런 문제가 없지만, 상속 받는 전역 클래스를 처리하는 문제가 골치를 썩이네요.

MFC의 AfxGetApp() 부분을 따라가 봤는데, AFX_MODULE_STATE에서 프레임웍을 초기화할 때 CWinApp의 인스턴스 포인터만 설정하는걸로 보이던데, 어떻게 이걸 가능하게 하는지 알 수가 없네요.. CWinApp가 Singleton 구조처럼 특정 설비를 가지고 있는 것도 아니고.. Singleton 구조라고 하더라도 파생 클래스는 어떻게? Slice 문제도 있을텐데 이게 템플릿으로 구현된 것도 아니고.. 크윽.

어떤 방법을 써야할지.. 난감..

sixmen의 이미지

질문을

class CMyApp : public CWinApp {};

일 때

CWinApp theApp;

이면 CMyApp 포인터를 얻을 수 없지 않느냐로 이해를 했는데 맞게 이해한건지 모르겠습니다. 당연히 이렇게 하면 CMyApp의 인스턴스를 얻을 수 없겠죠.

MFC의 경우

CMyApp theApp;

로 선언하는 거고, 그렇다보니 저 코드는 프레임웍이 가지고 있는게 아니라 어플마다 각각 가지고 있어야 하죠.

여하튼 저렇게 하면 main이 실행되기 전에 CMyApp의 인스턴스가 만들어지고, 그에 따라 CWinApp::CWinApp이 실행되니 그 시점에서 this를 적당한 전역 변수에 저장해 두면 이후 사용할 수 있겠죠..

CWinApp::CWinApp(LPCTSTR lpszAppName)
{
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
ASSERT(afxCurrentWinApp == NULL);
pModuleState->m_pCurrentWinApp = this;
ASSERT(AfxGetApp() == this);
}

senah의 이미지

기반 클래스를 선언했고, 이걸 CStartApp()라고 했을 때 전역변수로 선언되었다면, 이 정보를 받는 어떤 클래스에 this를 넘겨줘서 main()내에서 그 포인터를 사용할 수 있기는 할텐데.. (전역 변수들이 어떤 순서로 생성되는지 문제가 남아있긴 하지만, 불가능하진 않으니 일단 보류하고) 이 방법은 시험해봤는데 제대로 동작하지 않더라구요.

CStartApp()에서 InitInstance()라는 가상 함수가 있다고 했을 때, CStartApp()를 상속받아 InitInstance()를 구현하고, main()에서 아까 저장된 this 포인터를 사용하여 InitInstance()를 호출했을 때 상속받은 클래스의 가상 함수인 InitInstance()가 호출되도록 하는게 목적인데요.. 이 방법으로 시도해보았지만 기반 클래스의 InitInstance()가 호출되었습니다. (사용자의 상속 받은 클래스의 타입을 알 방법이 없으니)

그러니까 class CDerivedApp : public CStartApp에서 가상함수 InitInstance()를 다시 정의해주고, 사용자 코드에서 CDerivedApp app;로
선언했다면, 이미 코딩되어 있는 프레임웍의 초기화부분, CStartApp* example;이란 부분이 있다면 example->InitInstance()가 CStartApp를 상속받은 CDerivedApp::InitInstance()를 호출하는게 목적입니다. 한마디로 MFC와 비슷한 초기화 방식..

어떻게 구현하는지 감이 안 잡혀서 계속 무한 삽질 중 입니다.. ㅋ

sixmen의 이미지

#include <stdio.h>
 
// library
 
class CStartApp
{
public:
    CStartApp();
    virtual void InitInstance() {}
};
 
CStartApp *app;
 
CStartApp::CStartApp() { app = this; }
 
int main()
{
    app->InitInstance();
}
 
// user app
 
class CDerivedApp : public CStartApp
{
public:
    CDerivedApp() {}
    virtual void InitInstance()
    {
        printf("called\n");
    }
};
 
CDerivedApp theApp;

사용자가 상속 받은 클래스의 타입을 몰라도 쓸 수 있게 하려고 만들어진게 가상함수니까

CDerivedApp theApp;

로 인스턴스를 확실히 상속 받은 클래스의 것으로 했고, InitInstance가 virtual 함수가 분명하다면 호출이 되는게 정상이겠죠

만약 그래도 안 된다면 CStartApp의 인스턴스가 두개 인거 아닐까요? 어딘가 CStartApp theApp; 도 선언을 했다면 CStartApp::CStartApp() 이 두번 호출될테고, 그럼 전역변수의 호출순서에 따라서 app 변수가 CStartApp을 가질 수도 있고, CDerivedApp을 가질 수도 있겠죠. 전자의 상황이라면 CDerivedApp::InitInstance가 호출안되는게 당연할테고요

senah의 이미지

말씀해주신 방법을 참고해서 이런 방법을 써봤습니다.

먼저, MFC의 AFX_MODULE_STATE처럼 프로그램이 시작될 때 가장 근본적인 정보를 저장하는 클래스를 하나 만들고, 이것을 싱글턴 방식으로 구현했습니다. 굳이 싱글턴 방식은 아니어도 되지만, 일단 정적 변수가 전역변수보다 먼저 처리되지 않을까하는 생각도 있어서요. 그리고, CStartApp()처럼 프로그램의 유일한 인스턴스 개체의 생성자에서 이 싱글턴 클래스에 인스턴스 포인터를 저장하도록 했습니다. (이게 좀 문제가 있는데, 뒤에..)

그리고, CStartApp() 클래스의 생성자에서 자신의 인스턴스 포인터를 여기에 저장하고, main()의 엔트리 진입점에는 AfxGetApp()와 비슷한 역할을 하는 함수, 그러니까 이 클래스의 인스터스 포인터를 얻는 메서드를 따로 구현하여 CStartApp*를 리턴하고, 이렇게 얻어진 포인터를 사용하여 InitInstance()를 호출하도록 했습니다.

결과부터 말하자면, 반쪽짜리 해결책이 되었네요. VC2005에서는 의도한대로 구현이 되었지만, gcc에서는 역시나 전역변수나 정적변수 생성 순서에 따른 문제가 있는지 쓰레기 포인터를 참조하며 에러를 일으켰습니다. 흠~ 굳이 여러 컴파일러에서 컴파일되어야 할 필요는 없어서 해결됐다고 볼 수 있지만, 좀 반쪽짜리가 되어버렸네요. (정적변수나 전역변수 생성 순서는 컴파일러 구현마다 다르겠죠? 표준에 있을라나..)

답변 주신 sixmen님 감사합니다. 좋은 힌트가 되었습니다. 저의 허접한 삽질에도 불구하도 반쪽이나마 해결책을 찾아낸건 전적으로 님의 도움입니다. 고맙습니다~

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.