'C++'에 해당되는 글 58건
- 2009/08/06 [VS 2003] 웹 서비스 연동시 문제점 발생 - 구성 섹션 system.data을(를) 인식할 수 없습니다. - 해결방법
- 2009/06/19 프로세스 목록 가져오기 및 프로세스 찾기 / 종료시키기
- 2009/05/30 'fd_set' : 'struct' 형식 재정의 해결..
- 2009/05/30 'InitializeCriticalSectionAndSpinCount' 'SwitchToThread' 'TryEnterCriticalSection' : 인수 종속성 해결..
- 2008/12/30 MultiThread 예제 분석 (2)
- 2008/06/04 C++ 객체 팩토리(Object Factory)
- 2008/05/15 [C++] 타일 만들기.. (1)
- 2008/05/05 [C++, 템플릿] 템플릿의 부분 특화 (3)
- 2008/05/05 [C++, 템플릿] 템플릿 템플릿 인자를 통한 단위전략 확장
- 2008/05/04 [C++, 템플릿] 단위전략... (3)
- 2008/05/03 [C++, 템플릿] 부분 특화를 통한 함수 오버로딩??
- 2008/04/24 Modern C++ Design 책을 보면서 단위 전략 클래스 구현.. 멋지네요..
- 2008/04/17 Console 모드의 Printf 와 비슷한 기능을 하는 함수...
- 2008/04/02 [C++] UML 패턴 화살표의 의미 ( Composite Pattern )
- 2008/04/02 [C++] UML 패턴 화살표의 의미 ( Iterator Pattern )
[VS 2003] 웹 서비스 연동시 문제점 발생 - 구성 섹션 system.data을(를) 인식할 수 없습니다. - 해결방법

VS2008에서 .NET 2.0 으로 개발한 웹 서비스를 VS 2003에서 연동이 가능한데요.
아래의 이미지 처럼 "웹 참조 추가"를 통해서 연동이 가능합니다.
정상적으로 연동이 된다면 아래의 화면 처름 보여지게 됩니다.
하지만 아래와 같은 오류가 날때가 있어요.
위의 내용처럼 오류가 난걸 확인해 봤더니..
해당 폴더의 machine.config 의 내용을 보니
<system.data><DbProviderFactories><add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite, Version=1.0.65.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139" /></DbProviderFactories></system.data>
위와 같은 구문이 보였습니다.
헉.. SQLite 라는걸 설치를 하면 여기에 추가 되는걸 이제야 알았네요.. -.-
그래서 이 부분을 삭제를 했습니다.
그리고 나서 프로젝트를 닫고 새로 연 다음에 "웹 참조 추가"를 누르면 정상적으로 되는걸 확인할 수 있습니다.
// 프로세스 찾기
#include <tlhelp32.h>
bool GetProcessModule(DWORD dwPID, char* sProcessName);
bool ProcessFind(char* strProcessName)
{
HANDLE hProcessSnap = NULL;
BOOL bRet = FALSE;
PROCESSENTRY32 pe32 = {0};
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == (HANDLE)-1)
return false;
pe32.dwSize = sizeof(PROCESSENTRY32);
//프로세스가 메모리상에 있으면 첫번째 프로세스를 얻는다
if (Process32First(hProcessSnap, &pe32))
{
BOOL bCurrent = FALSE;
MODULEENTRY32 me32 = {0};
do
{
bCurrent = GetProcessModule(pe32.th32ProcessID,strProcessName);
if(bCurrent)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
if(hProcess)
{
// 이 주석을 없애면 프로세스를 죽이는것도 가능하다.
//if(TerminateProcess(hProcess, 0))
//{
// unsigned long nCode; //프로세스 종료 상태
// GetExitCodeProcess(hProcess, &nCode);
//}
//printf("process exit\n");
CloseHandle(hProcess);
return true;
}
}
}
while (Process32Next(hProcessSnap, &pe32)); //다음 프로세스의 정보를 구하여 있으면 루프를 돈다.
}
CloseHandle (hProcessSnap);
return false;
}
bool GetProcessModule(DWORD dwPID, char* sProcessName)
{
HANDLE hModuleSnap = NULL;
MODULEENTRY32 me32 = {0};
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
if (hModuleSnap == (HANDLE)-1)
return (FALSE);
me32.dwSize = sizeof(MODULEENTRY32);
//해당 프로세스의 모듈리스트를 루프로 돌려서 프로세스이름과 동일하면
//true를 리턴한다.
if(Module32First(hModuleSnap, &me32))
{
do
{
printf("process name : %s\n", me32.szModule);
if( strcmp(me32.szModule, sProcessName) == 0 )
{
CloseHandle (hModuleSnap);
return true;
}
//if(me32.szModule == sProcessName)
//{
// CloseHandle (hModuleSnap);
// return true;
//}
}
while(Module32Next(hModuleSnap, &me32));
}
CloseHandle (hModuleSnap);
return false;
}
----------------- 사용할때 -------------------
// 런처가 돌고 있는지를 판단한다. (제가 한건 해당 프로세스가 돌고 있는지를 찾는 코드로 재사용 했음)
if( ProcessFind("CruiseLauncher.exe") == false )
{
MessageBox(NULL, "CruiseLauncher가 실행중이지 않습니다.", "오류", MB_OK);
exit(0);
}
#define WIN32_LEAN_AND_MEAN // 거의 사용되지 않는 내용은 Windows 헤더에서 제외합니다.
windows.h 위에 디파인을 추가 합니다.
'InitializeCriticalSectionAndSpinCount' 'SwitchToThread' 'TryEnterCriticalSection' : 인수 종속성 해결..

c:\gsiproject\영호형회사\rfc\영호형_프로젝트\300.cruise\200.cruisemainproject\unit-module\cruise\network\lock.cpp(46): error C3861: 'InitializeCriticalSectionAndSpinCount': 인수 종속성을 조회해도 식별자를 찾을 수 없습니다.
c:\gsiproject\영호형회사\rfc\영호형_프로젝트\300.cruise\200.cruisemainproject\unit-module\cruise\network\lock.cpp(21): error C3861: 'SwitchToThread': 인수 종속성을 조회해도 식별자를 찾을 수 없습니다.
c:\gsiproject\영호형회사\rfc\영호형_프로젝트\300.cruise\200.cruisemainproject\unit-module\cruise\network\lock.cpp(59): error C3861: 'TryEnterCriticalSection': 인수 종속성을 조회해도 식별자를 찾을 수 없습니다.
위와 같은 오류가 난다면.
#ifndef _WIN32_WINNT // Windows XP 이상에서만 기능을 사용할 수 있습니다.
#define _WIN32_WINNT 0x0501 // 다른 버전의 Windows에 맞도록 적합한 값으로 변경해 주십시오.
#endif
이 코드를 stdafx.h의 window.h 선언 위쪽에 추가해 준다.
/* file Main.cpp
*
* This program is an adaptation of the code Rex Jaeschke showed in
* Listing 4 of his Nov 2005 C/C++ User's Journal article entitled
* "C++/CLI Threading: Part II". I changed it from C++/CLI (managed)
* code to standard C++.
*
* One hassle is the fact that C++ must employ a free (C) function
* or a static class member function as the thread entry function.
*
* This program must be compiled with a multi-threaded C run-time
* (/MT for LIBCMT.LIB or /MTd for LIBCMTD.LIB).
*
* John Kopplin 7/2006
*/
#include <stdio.h>
#include <windows.h> // for HANDLE
#include <process.h> // for _beginthread()
static bool interlocked = false; // change this to fix the problem
const int maxCount = 100000000;
static LONG value = 0; // under Windows Server 2003 you
// could use LONGLONG here
unsigned __stdcall TMain(void* arg)
{
if ( interlocked )
{
for ( int i = 1; i <= maxCount; i++ )
{
InterlockedIncrement(&value); // under Windows Server 2003 you
// could use InterlockedIncrement64() here
}
}
else
{
for ( int i = 1; i <= maxCount; i++ )
{
++value;
}
}
return 3; // thread exit code
}
int main()
{
// In this program we create 3 threads and request that their
// entry-point-function be the TMain() function which is a
// free (C) function and hence causes no problems for
// _beginthreadex()
HANDLE hth1;
unsigned uiThread1ID;
(원형) unsigned long _beginthread( void (*lpThreadEntryPoint)(void* lpArgList), unsigned uStackSize, void* lpArgList );
lpThreadEntryPoint 는 스레드가 시작된 함수의 주소인데, 이 함수에는 CreateThread()와 같이 한 개의 32비트 인자인 lpArgList가 있으며 이 값을 _beginthread()에 전달해야 한다. 이 함수는 void 타입으로 정의되어 CreateThread()와 조금 차이가 있다. 이 함수는 종료시까지 실행되는데 값을 반환하지는 않는다. uStackSize는 CreateThread()에서와 동일한 의미를 가지는데 스레드 스택의 베이스에서 커멧될 바이트 수이며, 이 값을 0으로 하면 윈도우는 부모 스레드에서 커멧된 것과 동일한 양의 메모리를 커멧힌다.
_beginthreadex() 는 CreateThread()와 완전히 같고 SECURITY_ATTRIBUTES 포인터와 시작 플래그(0이나 CREATE_SUSPENDED), 스레드 ID를 받는 포인터를 인자로 받는다. 일시 정지된 상태에서 스레드를 시작하거나 PostThreadMessage()에 스레드 ID를 사용하려면 _beginthreadex()를 사용해야 한다. _beginthread()
로 시작한 스레드만이 종료 코드를 설정할 수 있으므로 이때에도 필요하다.
hth1 = (HANDLE)_beginthreadex( NULL, // security
0, // stack size
TMain, // entry-point-function
NULL, // arg list
CREATE_SUSPENDED, // so we can later call ResumeThread()
&uiThread1ID );
if ( hth1 == 0 )
printf("Failed to create thread 1\n");
DWORD dwExitCode;
스레드에서 return 문이 등장하면 윈도우는 ExitThread()를 대신 호출해서 return 문에 전달된 값을 전달해 주는데, 이 때문에 입구 함수는 DWORD의 반환값을 갖는다.
스레드를 시작하는 방법이 여러가지 있고 각각의 종료 함수도 다르므로 스레드에서는 단순히 return문을 사용하는 것이 좋다.
GetExitCodeThread( hth1, &dwExitCode ); // should be STILL_ACTIVE = 0x00000103 = 259
printf( "initial thread 1 exit code = %u\n", dwExitCode );
HANDLE hth2;
unsigned uiThread2ID;
hth2 = (HANDLE)_beginthreadex( NULL, // security
0, // stack size
TMain, // entry-point-function
NULL, // arg list
CREATE_SUSPENDED, // so we can later call ResumeThread()
&uiThread2ID );
if ( hth2 == 0 )
printf("Failed to create thread 2\n");
GetExitCodeThread( hth2, &dwExitCode ); // should be STILL_ACTIVE = 0x00000103 = 259
printf( "initial thread 2 exit code = %u\n", dwExitCode );
HANDLE hth3;
unsigned uiThread3ID;
hth3 = (HANDLE)_beginthreadex( NULL, // security
0, // stack size
TMain, // entry-point-function
NULL, // arg list
CREATE_SUSPENDED, // so we can later call ResumeThread()
&uiThread3ID );
if ( hth3 == 0 )
printf("Failed to create thread 3\n");
GetExitCodeThread( hth3, &dwExitCode ); // should be STILL_ACTIVE = 0x00000103 = 259
printf( "initial thread 3 exit code = %u\n", dwExitCode );
// If we hadn't specified CREATE_SUSPENDED in the call to _beginthreadex()
// we wouldn't now need to call ResumeThread().
ResumeThread( hth1 ); // Jaeschke's // t1->Start();
ResumeThread( hth2 ); // Jaeschke's // t2->Start();
ResumeThread( hth3 ); // Jaeschke's // t3->Start();
// In C++ the process terminates when the primary thread exits
// and when the process terminates all its threads are then terminated.
// Hence if you comment out the following waits, the non-primary
// threads will never get a chance to run.
WaitForSingleObject( hth1, INFINITE ); // Jaeschke's t1->Join()
WaitForSingleObject( hth2, INFINITE ); // Jaeschke's t2->Join()
WaitForSingleObject( hth3, INFINITE ); // Jaeschke's t3->Join()
GetExitCodeThread( hth1, &dwExitCode );
printf( "thread 1 exited with code %u\n", dwExitCode );
GetExitCodeThread( hth2, &dwExitCode );
printf( "thread 2 exited with code %u\n", dwExitCode );
GetExitCodeThread( hth3, &dwExitCode );
printf( "thread 3 exited with code %u\n", dwExitCode );
printf( "After %d operations, value = %d\n", 3 * maxCount, value );
// under Windows Server 2003 you
// could use %I64d
// The handle returned by _beginthreadex() has to be closed
// by the caller of _beginthreadex().
CloseHandle( hth1 );
CloseHandle( hth2 );
CloseHandle( hth3 );
printf("Primary thread terminating.\n");
}
여러개의 객체를 생성하는 코드를 아래와 같이 일반적으로 사용합니다.
class Shape {};
class Line_ : public Shape { public: Line_() { printf("create Line\n"); } };
class Polygon_ : public Shape { public: Polygon_() { printf("create Polygon\n"); } };
class Circle_ : public Shape { public: Circle_() { printf("create Circle\n"); } };
namespace DrawingType
{
const int
LINE = 1,
POLYGON = 2,
CIRCLE = 3;
};
Shape* Load( int drawingType )
{
Shape* p;
switch( drawingType )
{
using namespace DrawingType;
case LINE:
{
p = new Line_();
}
break;
case POLYGON:
{
p = new Polygon_();
}
break;
case CIRCLE:
{
p = new Circle_();
}
break;
}
return p;
}
사용은 아래와 같이
Shape* p;
p = Load( DrawingType::LINE );
delete p;
p = Load( DrawingType::POLYGON );
delete p;
p = Load( DrawingType::CIRCLE );
delete p;
하지만 이런 코드의 경우 객체의 추가로 인한 오버헤드가 커지게 됩니다.
그럼 이런 경우 어떻게 조금더 최적화가 가능해질것인가를 생각해 보면 아래와 같이 될 수 있습니다.
//
class ShapeFactory
{
ShapeFactory() {}
public:
typedef Shape* (*CreateShapeCallback)();
static ShapeFactory& GetInstance()
{
static ShapeFactory sf;
return sf;
}
private:
typedef std::map< int, CreateShapeCallback > CallbackMap;
public:
bool RegisterShape( int ShapeId, CreateShapeCallback CreateFn )
{
return callbacks_.insert(
CallbackMap::value_type( ShapeId, CreateFn ) ).second;
}
bool UnRegisterShape( int ShapeId )
{
return callbacks_.erase( ShapeId ) == 1;
}
Shape* CreateShape( int ShapeId )
{
CallbackMap::const_iterator I = callbacks_.find( ShapeId );
if( I == callbacks_.end() )
return NULL;
return (I->second)();
}
private:
CallbackMap callbacks_;
};
namespace
{
Shape* CreateLine()
{
return new Line_;
}
Shape* CreatePolygon()
{
return new Polygon_;
}
Shape* CreateCircle()
{
return new Circle_;
}
};
// 사용할때
// 객체 레지스트리에 등록
ShapeFactory::GetInstance().RegisterShape( DrawingType::LINE, CreateLine );
ShapeFactory::GetInstance().RegisterShape( DrawingType::POLYGON, CreatePolygon );
ShapeFactory::GetInstance().RegisterShape( DrawingType::CIRCLE, CreateCircle );
p = ShapeFactory::GetInstance().CreateShape( DrawingType::LINE );
delete p;
p = ShapeFactory::GetInstance().CreateShape( DrawingType::POLYGON );
delete p;
p = ShapeFactory::GetInstance().CreateShape( DrawingType::CIRCLE );
delete p;
객체를 레지스트리에 등록하고
사용하게 됩니다.
이때 더 나아간다면 템플릿을 사용해서 예외 처리를 조금더 기능적으로 해줄 수 있습니다.
타일을 하나하나 마우스로 찍어서 하는건 너무 노가다라고 생각 되고,
스타 크래프트의 맵 에디터는 신의 경지고 ^^..
우선 아래와 같은 실행 파일을 하나 제작해 봤습니다.
기본 토대는 여러가지의 카테고리를 가진 기본 타일이 존재 하고 그 타일의 주변을 감싸는 테두리를 검색한 후에 그 검색한 테두리 부분을 부드럽게 연결해 주는 겁니다.
관련코드 :
.
template< class T1, class T2 >
class Test
{
};
이런 클래스가 있다고 합시다.
우리는 이 클래스가 두개의 템플릿 인자를 받아 들인다는 걸 알고 있습니다.
Test< Ca, Cb > A;
A a;
이런 식이 되는 거죠.
그렇다면 저희가 작업 할때 Test 라는 템플릿 클래스를 놓고 특정 타입에 대해서
특화 되게 구현하고 싶은 경우가 있을거예요.
template< class T1, class T2 >
class Test {};
template <>
class Test< CT, CU > {};
이렇게 두개의 같은 클래스이지만 하나는 CT, CU의 명시적으로 특화된 클래스가 또 하나 존재를 하게 되면 만약 코딩을 통해서 CT, CU가 들어 오게 되면 아래의 클래스가 동작 되게 되는거죠.
바로 이게 부분 특화 입니다.
이때 부분적으로 특화를 하고 싶다면 아래와 같은 클래스를 하나 추가 하면 되요
template < class T1 >
class Test< T1, CU > {};
앞의 T1은 모든 객체를 다 받아 들이고 뒤의 CU는 명시적으로 선언을 해놨어요.
이게 바로 부분 특화라는 거죠 ^^
하나더 해볼까요?.
Button 이라는 클래스가 있다고 합시다. 모든 Button과 CU에 대해서 특화 시키고자 한다면...
template < class T1 >
class Test< Button<T1>, CU > {};
이렇게 하면 되죠 ^^
템플릿의 장점은 참 많네요..
ps. 지적 및 조언 및 질문 해주시면 고맙겠습니다.
제목이 참 아리송 합니다. ^^
하지만 내용을 읽고 나면 이해가 될거예요..
이전의 포스트 를 우선 보시고 이 내용을 꼭 보셔야 합니다.
우선 이전의 내용을 한번 짚어 보면...
단위 전략을 통해서 객체 생성에 new, malloc를 선택적으로 해줄 수 있다는 거죠.
typedef WidgetManager< OpNewCreator<CTest> > MyWidgetMgr;
MyWidgetMgr a;
이런 코드를 사용해서 CTest를 특화 되게 구현할 수 있게 되는거죠.
즉, CTest 의 객체를 new, malloc를 선택해서 처리가 가능하다는 말이 됩니다.
그런데 이때 고민이 좀 됩니다.
뭐냐 하면 내부에서
CTest* p = CreationPolicy().Create( p2 );
이 코드를 사용했어요. CTest 를 특화 했기 때문에 CTest만을 사용하게 되는거예요.
난 다른 객체들도 메니져 안에서 다 관리 해주고 할려고 하면 에러가 나서 안되는거죠.
그래서 이 포스트의 주제가 나옵니다.
기본 선언은 비슷하게 구현하되 내부에서 다른 객체를 생성하는 것도 가능하게 말이죠.
우선 아래의 코드를 한번 보고 해요.
template < class T >
struct OpNewCreator
... 생략
template < class T >
struct MallocCreator
... 생략
template < template < class > class CP >
class CTestMgr : public CP<CTest>
{
public:
CTestMgr()
{
CTest2* ptest2 = CP<CTest2>().Create();
}
~CTestMgr() {}
};
위의 코드를 보면 이런 코드가 보입니다.
template < template < class > class CP >
이게 바로 템플릿 템플릿 인자를 통한 단위전략의 중요한 부분이라고 보여 집니다.
즉 이렇게 해서 CP<CTest> 라고 명시적으로 적어 주게 되면 아래와 같은 코드를 통해서 메니져를
처리할 수 있습니다.
typedef CTestMgr< OpNewCreator > MyTestMgr;
원래는 OpNewCreator 에 다른 인자를 더 넣어 주어야 하지만 명시적으로 처리가 되었기 때문에
추가를 해줄 필요가 없게 됩니다.
이렇게 하고 나면...
바로 아래와 같이 내부에서 다른 객체를 사용할 수 있게 됩니다.
CTest2* ptest2 = CP<CTest2>().Create();
어때요?.. CP의 단위 전략을 사용하면서 내가 원하는 다른 객체를 적용할 수 가 있게 됩니다.
Tip.
여기서 보시면 CP에 CTestMgr를 연결할 수도 있게 되요.
CTestMgr* p = CP<CTestMgr>().Create(); 이렇게요.
이건 나중에 템플릿을 여러개로 사용하면서 현재 클래스를 체크를 해준다거나 하는
여러가지 기능을 처리 하는 부분에서 또 설명이 나오네요..
오늘 내용은 여기 까지 ^^
ps. 지적 및 조언 및 질문 해주시면 고맙겠습니다.
예로 들 것은 객체를 생성할때 template를 통해서 new와 malloc를 선택적으로 해주기 위함입니다.
다른 방법들도 몇가지 덧 붙여서 설명하겠습니다.
단위 전략의 설명은 책에 잘 나와 있으니 패스 ^^
우선 단위 전략에 필요한 인터페이스 즉, new, malloc로 구현된 두개의 struct 문입니다.
template < class T >
struct OpNewCreator
{
static T* Create()
{ return new T }
};
template < class T >
struct MallocCreator
{
static T* Crate()
{
void* buf = std::malloc(sizeof(T));
if(!buf) return 0;
return new(buf) T;
}
};
두개의 단위 전략을 생성 했다면 아래의 단위 전략을 사용할 클래스를 하나 제작 합니다.
template < class CreationPolicy >
class WidgetManager : public CreationPolicy
{
public:
WidgetManager()
{
CTest* p = CreationPolicy().Create();
...
}
...
};
어때요?.. 멋지지 않나요?.. CreationPolicy 에 들어 오는 타입에 따른 Create() 를 호출하게 되는 구조입니다.
사용할때는 아래와 같이 적용이 가능해 집니다.
typedef WidgetManager< OpNewCreator<CTest> > MyWidgetMgr;
MyWidgetMgr a;
즉, OpNewCreator 를 MallocCreator 로 바꾸면 다른 기능을 할 수 있다는 거죠.
이게 바로 단위 전략이라는 거 같네요..
이것을 조금더 자연스럽게 해주는 기능이 또 있습니다.
바로 템플릿 템플릿 인자를 통해서요.
이 설명은 좀더 정리 되면 적도록 하구요..
다른 내용을 주제로 더 적도록 하겠습니다.
위의 단위 전략의 Create() 함수에 다른 인자를 더 넣는건 어떻게 할까.. 고민을 해봤어요.
즉 template 의 인자를 하나더 주자는 예기죠.
아래와 같이 바꾸어 보겠습니다.
template < class T, class U >
struct OpNewCreator
{
static T* Create( U* u )
{
...
}
};
이렇게 말이죠. U 의 타입을 받아서 T를 생성한다.
이때는 아래와 같이 타입을 하나더 넣어 주어야 합니다.
typedef WidgetManager< OpNewCreator<CTest, CTest2> > MyWidgetMgr;
이렇게 해서 두개의 타입을 연결하였습니다.
근데 이때, U 에 들어 가는 타입을 디폴트 타입으로 지정하면 어떨까 생각 했죠.
template < class T, class U = CTest2 >
...
이렇게 하면 뒤어 인자를 적어줄 필요가 없게 됩니다.
typedef WidgetManager< OpNewCreator<CTest> > MyWidgetMgr;
바로 이렇게 말이죠.
그렇다면 Create( U* u ) 의 U 인자를 특정 타입으로 특화 하면 어떨까 생각 했죠.
template < class T, class U = CTest2 >
struct OpNewCreator
{
static T* Create( CTest2* u )
{
...
}
};
이렇게 하게 되면 WidgetManager 내부에서 제한적인 코딩이 가능해 집니다.
즉, CTest2 의 인자만을 Create()에 집어 넣지 않으면 컴파일 에러가 발생하게 되죠.
다른 인자를 집어 넣는 실수를 막을 수 있을거 같네요.
CTest* p2 = new CTest();
CTest* p = CreationPolicy().Create( p2 );
이렇게 하면 p2가 CTest2가 아니기 때문에 에러가 나게 됩니다. ^^
Tip.
template 를 사용하게 되면 프로그램의 실수를 컴파일 타임에 잡아 주기 위한 기능으로도
적용이 되고 있습니다. 여러사람이 작업을 하거나 제한적인 코딩을 통한 에러를 막기 위한 방법으로
괜찮은 기능인거 같아요.
이상..
잘못된 내용이나 궁금증은 언제든 환영합니다. ^^
개인적으로 저에게 가장 많은 생각을 하게 만든건 "Modern C++ Design" 입니다.
현재 1단원만 몇번째 보고 있지만 완전하게 이해 하기가 힘들군요.
하지만 C++ 에서 template 가 왠지 저를 잠못자게 하네요..
특화.. 라는 말을 많이 듣게 되는데요.
가령 예를 들어서 SmartPtr<T>템플릿을 사용하면서, SmartPtr<CTest> 에 대한 모든 멤버 함수를 특화 시킬 수 있습니다.. 이런 말이 나오네요.
제가 이해 하기로는 CTest 함수를 템플릿에 넣어서 특화 시킬 수 있다. 특별하게 처리할 수 있다
전 이렇게 이해가 됩니다...
그렇다면 이번 주제는 함수를 오버로딩하는 건데요.
C++ 에서는 아래와 같이 함수를 오버로딩할 수 있습니다.
void Test(int a) {}
void Test(string a) {}
이런 식이죠.
우선 이것을 지나기 전에 전 기존에 template 가 cpp 쪽의 함수 구현이 어떻게 되어야 하는지 몰랐네요 ^^
헤더 파일에 아래와 같은 클래스와 함수가 있다고 가정을 합시다.
template < class T>
CTest
{
...
void Fun();
};
이렇게 있다고 했을때 cpp 에서는 똑같이 함 적어 볼께요.
template < class T >
void CTest::Fun() {}
아 이렇게 하면 두개의 에러가 나옵니다.
> 함수 정의를 기존 선언과 일치시킬 수 없다와 템플릿 클래스를 사용하려면 템플릿 인수 목록이 있어야 합니다.
이런 에러가 나오게 됩니다.
그래서 조금 다르게 해서 아래와 같이 작성하면 에러가 없어 집니다.
template < class T >
void CTest< T >::Fun() {}
이건 알고 계신다구요?.. -.- 전 이제까지 몰랐답니다. ㅋㅋ..
여기 까지는 대부분 아는 내용일테구요..
이제 여기 까지 하게 되면 멤버 함수 특화 라는 말이 나오게 되요. @.@
이 말도 정말 이해가 안되더군요 ^^..
하지만 특화라는 말은 template 의 T의 성분을 특정 타입으로 지정한다..
이렇게 전 아직 이해 한답니다. <--- 이거 잘못된건지 아시면 꼭 지적 부탁 드려요..
그래서 아래와 같이 함수 특화를 해볼께요..
template <>
void CTest< char >::Fun() {}
이렇게 해서 char 형태로 특화를 했습니다.
이렇게 하고 나면 어떤 현상이 발생 하냐 하면요..
이 클래스를 사용하는 부분에서.
typedef CTest<int> A;
A a;
이렇게 작성했다고 합시다.
우리가 이전에 Fun() 함수는 char 형태로만 특화 되어 있는걸 아시죠?
즉, int 형을 특화 시켰기 때문에 에러가 발생 합니다.
이것만 가지고도 사용자의 실수를 방지 할 수 있는 좋은 수단이 되지요. ^^
그렇다면 내가 원하는 특정한 타입을 T로 받아 들이고 그 타입에 따른 Fun() 함수를 따로 구현하고 싶을때
필요한 타입을 다 제작을 합니다.
template < class T >
void CTest<T>::Fun() {}
template <>
void CTest<char>::Fun() {}
template <>
void CTest<int>::Fun() {}
이렇게 3가지를 구현했습니다.
T의 값이 int, char 일때는 아래 두개의 함수로 분기 되게 됩니다.
그리고 나머지 모든 값은 제일 위의 함수로 분기 되죠.
어때요?.. 전 이거 보고 너무 좋았답니다. ^^
Tip.
위의 3개의 함수를 보시면 위쪽은 class T 가 되어 있는걸 아시겠죠?.
아래는 안되어 있구요..
개인적으로 다 해보세요.. 위와 같이 안하면 에러가 나요 ^^
왜냐구요?.. 아직 잘 모르겠어요 ㅋㅋ.. 아시면 꼭 얘기 부탁 해요.. ^^
책을 2, 3번 정도 읽으니 조금 이해가 되네요.
아직 100%는 아니지만 ^^
처음에는 템플릿으로 참 알아 보지도 못하는 것을 구현하기에.. 난해 했지만.
지금 조금 이해가 되는 부분에서 느껴 지는 것은
템플릿을 사용해서 컴파일 타임에서 오류를 잡아 주거나,
메모리 생성하는 규칙을 템플릿으로 typedef 잡아서 사용하거나 하는 부분들이
잘만 활용하면 상속 만큼이나 아니면 더 많은 부분을 효과적으로 구현하게
해주는 것인지도 모르겠다.
아래의 예를 잠깐 보면..
// TemplateTest.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//
#include "stdafx.h"
template <class T>
struct OpNewCreator
{
static T* Create()
{
printf("OpNewCreator - new 사용\n");
return new T;
}
};
template <class T>
struct MallocCreator
{
static T* Create()
{
printf("MallocCreator - malloc 사용\n");
void* buf = std::malloc(sizeof(T));
if(!buf) return 0;
return new(buf) T;
}
};
class Widget
{
public:
Widget() { printf("Widget 생성자 호출\n"); }
~Widget();
};
class CTest : public Widget
{
public:
CTest() { printf("Widget 생성자 호출\n"); }
~CTest() {}
};
template < template <class Created> class CreationPolicy >
class WidgetManager : public CreationPolicy< Widget >
{
public:
WidgetManager()
{
Dosomething();
}
~WidgetManager() {}
void Dosomething()
{
CTest* pw = CreationPolicy<CTest>().Create();
}
};
typedef WidgetManager< OpNewCreator > MyWidgetMgr_New;
typedef WidgetManager< MallocCreator > MyWidgetMgr_Malloc;
int _tmain(int argc, _TCHAR* argv[])
{
MyWidgetMgr_New a;
MyWidgetMgr_Malloc b;
return 0;
}
어쩌면.. 위의 typedef 부분인거 같다.
내가 new를 사용해서 객체를 할당 한다거나 하다가 상황에 따라서 malloc를 사용해야 한다고 하면
코드를 고치기 보다 typedef 부분을 수정해 주므로 모든게 해결이 된다.
아래는 실행된 모습이다.
하지만 여기서 궁금한건...
Widget를 Class 부분에서 미리 정의 해 주는 부분인데.. 잘 이해가 되지 않는다.
MFC 에서 ListBox 에 내용을 추가 하는 코드로 테스트 해본겁니다.
void UTRACE(char* fmt...)
{
//CString output;
CTime time = CTime::GetCurrentTime();
std::ostringstream outstream;
outstream << "[" << time.GetHour() << ":" << time.GetMinute() << ":" << time.GetSecond() << "] ";
va_list ap; //argument pointer
char*p; int ival; double dval;
char pBuf[256]; // 버퍼용
va_start(ap, fmt); //make ap point to the final named arg
for(p=fmt; *p; p++) {
if(*p !='%'){
outstream << *p;
//putchar(*p);
continue;
}
switch(*++p){
case 'd':
ival = va_arg(ap, int); //get the argument and move on to the next
outstream << ival;
/*itoa(ival, pBuf, 10);
for(int i=0 ; i < (int)strlen(pBuf) ; i++)
{
putchar(pBuf[i]);
}*/
break;
case 'f':
dval = va_arg(ap, double); //get the argument and move on to the next
outstream << dval;
/*dval = atof( pBuf );
for(int i=0 ; i < (float)strlen(pBuf) ; i++)
{
putchar(pBuf[i]);
}*/
break;
case 's':
sarg = va_arg(ap, const char *); //get the argument and move on to the next
outstream << sarg;
break;
case 't':
outstream << "\t";
break;
default:
outstream << *p;
break;
}
}
va_end(ap);
g_pDebugListBox->AddString( outstream.str().c_str() );
g_pDebugListBox->SetCurSel( g_pDebugListBox->GetCount()-1 );
// 여기에서 콘솔 모드면 아래 코드를 적용하면 될듯 하다.
memset( pBuf, 0, sizeof(char) * 256 );
strcpy( pBuf, outstream.str().c_str() );
//p = pBuf;
printf( pBuf );
}
아래의 이미지는 Iterator 패턴을 사용해서 화살표가 의미 하는 것을 적어 보았다.
UML을 사용하다 보면 아직까지 익숙하지 않아서 화살표의 의미 자체가 조금 힘들게 받아 들여 진다.
아래의 이미지는 Iterator 패턴을 사용해서 화살표가 의미 하는 것을 적어 보았다.
TileEditor.zip
Prev
Rss Feed