GSI

MFC에서 타이머 사용하기 위한 방법입니다.

가끔 사용하다 보면 어떤건지 헷갈릴 때가 있습니다.

그래서 정리한 내용 입니다.


※ 메시지 맵에 아래 내용을 등록 합니다.

BEGIN_MESSAGE_MAP(CGroupChatWindow, CDialog)

...

ON_WM_TIMER()

...

END_MESSAGE_MAP()


※ 헤더파일에 함수를 선언 합니다.

afx_msg void OnTimer(UINT nIDEvent);


※ 함수는 아래와 같이 생성합니다.

void CTest::OnTimer(UINT nIDEvent)

{

    switch(nIDEvent)

    {

        case 1:

    // 내용을 여기 추가

            break;

    }


    //KillTimer(1); // 필요하면 이걸 사용해서 타이머 종료


}


※ 타이머 시작

SetTimer(1, 1000, NULL); // 이벤트 아이디 1, 1000ms(1초)


-- 이상 입니다. --


Posted by gsi
:

최근에 json 을 파싱할 일이 있어서 jsoncpp 를 사용하게 되었는데요.

Debug할때 오류가 나지 않는 부분이 Release를 하게 되면 아래와 같은 오류가 날 수 있습니다.


1>  코드를 생성하고 있습니다.

1>D:\jsoncpp-src-0.5.0\src\lib_json\json_value.cpp : fatal error C1083: 컴파일러 생성 파일 파일을 열 수 없습니다. '../../build/vs71/release/lib_json\json_value.asm': No such file or directory

1>LINK : fatal error LNK1257: 코드를 생성하지 못했습니다.


이때 해결 방법은 아래와 같습니다.


jsoncpp 프로젝트 저 같은 경우는 lib_json 입니다.


> 프로젝트 속성 페이지를 엽니다.

> C/C++ 탭의 "출력 파일"을 클릭합니다.

> "어셈블러 출력" 항목의 내용을 "목록 없음" 으로 변경합니다.


빌드 하고 사용하시면 오류가 없어 집니다.


Posted by gsi
:

Visual Studio 2003 으로 프로젝트를 구성중에

이 오류를 가끔 발견할때가 있다.

이 오류를 해결하기 위한 가장 간단한 방법은 Visual Studio를 재부팅 하면 된다.

하지만 개발중에 이 메시지를 보고 다시 재부팅 하기에는 짜증이 너무 난다.


이 오류를 해결하기 위해서 자료를 많이 찾아 봤다.

하지만 대부분 해결이 안되서 한참 찾던중에 해결방법을 찾았다.

결국 NatDbgDE.dll 파일에 오류가 있어서 패치가 나와 있던걸 발견했다.


Visual Studio 2003을 기준으로 설명한다.


1. C:\Program Files (x86)\Microsoft Visual Studio .NET 2003\Common7\Packages\Debugger 폴더로 간다.


2. NatDbgDE.dll 파일을 삭제 또는 백업(다른이름으로 변경)한다.


3. 첨부 자료에 있는 압축 파일 안에 있는 dll을 복사해서 같은 이름으로 변경한다.

  - 참고자료 링크를 클릭하면 다운로드 받는 주소가 나옵니다.

  - 여기 올려 놓은 압축파일을 사용해도 됩니다.


4. 그리고 VS툴을 켜서 확인해보면 문제가 없는걸 알 수 있다.


[참고자료]

http://bytepointer.com/articles/vs7.1_2003_on_win7_pdb_handle_leak_bug_unofficial_fix.htm


[압축파일]

vs7.1_on_win7_pdb_handle_leak_unofficial_fix_win10.zip


Posted by gsi
:

API-MS-WIN-CORE-KERNEL32-PRIVATE-L1-1-1.DLL

API-MS-WIN-CORE-PRIVATEPROFILE-L1-1-1.DLL

API-MS-WIN-SERVICE-PRIVATE-L1-1-1.DLL


위의 dll 때문에 오류가 나는 경우가 있다.


Debug 모드인지 체크 한다.

Release 모드일때는 문제가 없는 경우가 존재 한다.

이럴 경우 Release 모드로 전환후 디버깅을 걸수 있도록 하고 프로그램을 구현하는 방법을 사용하자.

 

 

Posted by gsi
:

Bullet3D 라이브러리를 계속 파오는 동안 벌써 몇달이 지난듯 하다.

 

계속해서 로봇의 움직임을 구동하기 위해서는 객체마다 조건(ex btHingeConstraint)을 걸어줘야 하며,

조건들을 움직이는건 일단 마무리가 되었다

이후에 계속해서 가지고 있던 고민은 다리 하나하나를 움직여야 하니 제일 끝의 객체를

위치 이동을 해보면 좋을거 같았다.

 

그래서 마우스로 객체를 움직일 수 있는 부분을 파 보았다.

두가지의 제어 방식이 보인다.

btGeneric6DofConstraint 이것과 btPoint2PointConstraint 두가지다

 

일단 btPoint2PointConstraint 이걸로 제어를 해보기로 했다.

아래의 코드는 움직임을 제어 하는 방법이다.

 

객체의 변수는 아래와 같이 선언

btPoint2PointConstraint* pConStraint;

 

이 코드는 매번 포인터 객체를 삭제 해야 한다.

if( pConStraint )
{
 m_dynamicsWorld->removeConstraint( pConStraint );
 delete pConStraint;
 pConStraint = NULL;
}

 

// 해당 btRigidBody 를 연결시켜 준다.

btVector3 p1(0, 0, 0);
pConStraint = new btPoint2PointConstraint( *m_rigs[0]->m_bodies[3], p1 );
m_dynamicsWorld->addConstraint( pConStraint, true );
pConStraint->m_setting.m_tau = 1;

 

// 위와 같이 연결하고 나서 아래와 같이 해당 이동값을 적어주면 객체가 움직이게 된다.
btVector3 newPivotB;
newPivotB.setZero();
newPivotB.setZ( 0.1 );
pConStraint->setPivotA( newPivotB );

 

근데 위와 같이 코드를 구성한 이후는 btRigidBody를 Constraint로 연결을 하고

pConStraint->setPivotA( newPivotB ); 를 하고 나면 계속해서 연결이 유지되고

계속해서 움직임을 할 수 있을거라 생각 했지만,

잘 안되서 포인터 객체를 없애고, 다시 연결후에

pConStraint->setPivotA( newPivotB ); 이걸 통해서 위치를 이동시킬 수 있는걸 알았다.

 

이제 앞으로 해야 할것

 

//
pConStraint = NULL;
pConStraint2 = NULL;

//
{
 btVector3 p1(0, 0, 0);
 pConStraint = new btPoint2PointConstraint( *m_rigs[0]->m_bodies[3], p1 );
 m_dynamicsWorld->addConstraint( pConStraint, true );
 pConStraint->m_setting.m_tau = 1;
}
{
 btVector3 p1(0, 0, 0);
 pConStraint2 = new btPoint2PointConstraint( *m_rigs[0]->m_bodies[6], p1 );
 m_dynamicsWorld->addConstraint( pConStraint2, true );
 pConStraint2->m_setting.m_tau = 1;
}

이 코드를 사용해서 해당 객체를 바닥에 고정 시킨다.

 

고정시켜 놓고 나서 시퀀스를 구성하고 하나하나 이동하면서 위치가 앞으로 이동하는지를 보자.

Posted by gsi
:

이 자료는

4족 로봇을 위한 기초자료로서 Bullet3D의 라이브러리 이해를 위해서 정리한 내용임

 

 

 

위의 내용은 로봇암의 한쪽 부분을 설명하기 위한 자료

오른쪽의 마젠타 색상의 사각형이 고정부 그기서 부터 옆으로 관절1, 관절2 형태로 취해진다.

관절2(파란색)을 키보드로 움직이면 중간의 관절1의 각도가 변해야 하는게 목표이다.

 

조금 움직였을때의 모습

 

관절2번을 아래로 회전하게 되면서 관절1번의 각도가 변한 모습이다.

 

위의 화면은 카메라 뷰에서 본 화면이며

객체마다 Axis가 표기 되어 있는데

빨간색 - X

녹색 - Y

파란색 - Z (화면 뒤쪽)

 

아래는 관련된 코드이다.

 

화면에 보이는 객체는 일단 모델링 데이터가 존재 해야 하기 때문에 그 모델링 데이터는

btCollisionShape* m_shapes[2];//BODYPART_COUNT];

이걸로 관리 된다.

이 포인터 객체에 해당 데이터가 생성이 된다.

m_shapes[0] = new btBoxShape(btVector3(fBodySize, fBodySize, fBodySize));
m_shapes[1] = new btBoxShape(btVector3(fBodySize, fBodySize, fBodySize));

이 객체들은 나중에 화면에 객체를 표현하기 위한 인스턴스 객체들이다.

 

아래 코드는 화면에 보여지는 3개의 객체를 만든 내용이다.

  // 높이 0.5에 기본 위치를 잡는다.
  float fHeight = 1.0;
  btTransform offset; offset.setIdentity();
  offset.setOrigin(positionOffset);  

  // root
  // 중앙의 둥근원형을 만든다.
  btVector3 vRoot = btVector3(btScalar(0.), btScalar(fHeight), btScalar(0.));
  btTransform transform;
  transform.setIdentity();
  transform.setOrigin(vRoot);
  // 무게를 0으로 하면 고정형태의 객체가 만들어 진다.
  m_bodies[0] = localCreateRigidBody(btScalar(0.), offset*transform, m_shapes[0]);

  // 다리1
  transform.setIdentity();
  btVector3 vBoneOrigin = btVector3(1.0, 1.0, 0);
  transform.setOrigin(vBoneOrigin);
  btVector3 vToBone = (vBoneOrigin - vRoot).normalize();
  btVector3 vAxis = vToBone.cross(vUp);
  m_bodies[1] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[1]);

  // 다리 2
  transform.setIdentity();
  vBoneOrigin = btVector3(2.0, 1.0, 0);
  transform.setOrigin(vBoneOrigin);
  vToBone = (vBoneOrigin - vRoot).normalize();
  vAxis = vToBone.cross(vUp);
  m_bodies[2] = localCreateRigidBody(btScalar(1.), offset*transform, m_shapes[1]);

 

위의 내용으로 화면에 보여지는 객체를 만들고 나면 이제 객체들을 연결하기 위한 조인트를 연결해 줘야 한다.

이걸 하지 않으면 무게 1짜리가 바닥으로 바로 떨어지게 된다. 중력이 있기 때문에.

 

아래 코드는 Hing를 추가 하는 모습이다.

나중에 서보 모터를 연동하기 위함이기 때문에 Hing라는 걸로 연결하도록 한다.

 

  btHingeConstraint* hingeC;
  //btConeTwistConstraint* coneC;

  btTransform localA, localB, localC, localD;

  float fAngle = 0;
  float fSin = sin(fAngle);
  float fCos = cos(fAngle);

  // hip joints
  localA.setIdentity();
  localB.setIdentity();
  localA.getBasis().setEulerZYX(0,0,0); 
  localA.setOrigin(btVector3(0.5, -0.5, 0));
  localB.getBasis().setEulerZYX(0,0,0); 
  localB.setOrigin(btVector3(-0.5, -0.5, 0));

  hingeC = new btHingeConstraint(*m_bodies[0], *m_bodies[1], localA, localB);
  hingeC->setLimit(DEG2RAD(0), DEG2RAD(90));
  m_joints[0] = hingeC;
  m_ownerWorld->addConstraint(m_joints[0], true);

  // hip joints
  localA.setIdentity();
  localB.setIdentity();
  localA.getBasis().setEulerZYX(0,0,0); 
  localA.setOrigin(btVector3(0.5, -0.5, 0));
  localB.getBasis().setEulerZYX(0,0,0); 
  localB.setOrigin(btVector3(-0.5, -0.5, 0));

  hingeC = new btHingeConstraint(*m_bodies[1], *m_bodies[2], localA, localB);
  hingeC->setLimit(0.0f, 0.0f);
  m_joints[1] = hingeC;
  m_ownerWorld->addConstraint(m_joints[1], true);

 

위의 코드와 같이 구성하게 되면 Hing로 3개의 객체가 연결이 되게 된다.

 

위의 모델에서 관절2를 움직이기 위한 코드는 아래와 같이 작성하면 된다.

 

 // 해당 모델을 선택된 상태로 가져와야 한다.

 // 이걸 하지 않으면 객체는 업데이트 자체가 되지 않는다. (왜 인지는 모르겠다.)

 m_rigs[0]->m_bodies[2]->setActivationState(DISABLE_DEACTIVATION);

 // Hing 객체를 찾는다. (미리 알고 있기 때문에 배열값을 바로 가져온다.)

 btHingeConstraint* hingeC = static_cast<btHingeConstraint*>(m_rigs[0]->GetJoints()[1]);
 if( hingeC )
 {
  btScalar hingeAngle = hingeC->getHingeAngle(); // 참고용
  btScalar hingeLow = hingeC->getLowerLimit(); // 참고용
  btScalar hingeHigh = hingeC->getUpperLimit(); // 참고용

  // 이걸 보면 알 수 있듯이 해당 객체의 Hing의 각도를 그냥 하나로 고정시켜서 키보드 값으로 이동 가능하게 처리 하였다.

  hingeC->setLimit(DEG2RAD(n_TestAngle), DEG2RAD(n_TestAngle));

  >> hingeC->setLimit(DEG2RAD(n_TestAngle), DEG2RAD(n_TestAngle), 0, 1, 0); 이걸로 고치면 움직임이 조금더 빠르다. 소프트한게 없어지고..

  // 모델 생성시 아래 코드도 그냥 0으로 바꾸면 움직임이 조금더 빨라진다.

  for (i = 0; i < 3; ++i)
  {
   m_bodies[i]->setDamping(0, 0); // 이 값이 크지면 중력의 영향을 천천히 받아서 모델이 천천히 떨어진다.
   m_bodies[i]->setDeactivationTime(0);
   m_bodies[i]->setSleepingThresholds(0, 0);
  }

 

  // 아래 코드는 굳이 하지 않아도 동작은 한다.
  hingeC->enableAngularMotor(false, 0, 0);
 }

 

아래는 관련 코드입니다.

 

BulletTestApp_분석용_Constraint.zip

 

 

 

 

 

 

 

Posted by gsi
:


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 라는걸 설치를 하면 여기에 추가 되는걸 이제야 알았네요.. -.-

그래서 이 부분을 삭제를 했습니다.
그리고 나서 프로젝트를 닫고 새로 연 다음에 "웹 참조 추가"를 누르면 정상적으로 되는걸 확인할 수 있습니다.
Posted by gsi
:

// 프로세스 찾기
#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);
 }

Posted by gsi
:

c:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Include\WinSock2.h(109): error C2011: 'fd_set' : 'struct' 형식 재정의

#define WIN32_LEAN_AND_MEAN  // 거의 사용되지 않는 내용은 Windows 헤더에서 제외합니다.

windows.h 위에 디파인을 추가 합니다.
Posted by gsi
:


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 선언 위쪽에 추가해 준다.
Posted by gsi
:

MultiThread 예제 분석

C++ 2008. 12. 30. 14:56 |

/*  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;

CreateThread() 함수를 사용했을 때 스레드에서 Win32 함수만을 사용한다면 문제가 없지만 C 런타임 라이브러리 호출을 하려면 _beginthread()나 _beginthreadex() 를 사용해야 한다.

(원형) 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;

 스레드가 수행되는 동안에 GetExitCodeThread()를 호출하면 종료 코드 STILL_ACTIVE를 반환한다.
 스레드에서 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().

인자 dwFlags는 스레드의 초기 스케쥴링을 결정하는데 0의 값으로 설정하면 윈도우는 스레드가 바로 수행되도록 스케쥴 하고, 0이 아닌 CREATE_SUSPENDED의 값은 핸들을 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().

대부분의 스레드 관리 함수는 스레드 핸들을 필요로 하는데 그 중 몇몇만이 스레드 ID를 필요로 하고 가장 필요로 하는 함수는 PostThreadMessage()이다. 모든 Win32 핸들과 같이 스레드 핸들의 사용이 끝나면 CloseHandle() 함수를 사용하여 스레드 핸들을 닫아야 한다. 이것은 스레드 자체에는 아무런 영향을 미치지 않는다. 스레드 핸들을 닫는 것은 스레드를 끝내는(Kill) 것과는 다르며, 이렇게 하지 않을 경우 프로그램이 종료할 때까지 시스템 자원이 할당된 채로 남게 된다.

    CloseHandle( hth1 );
    CloseHandle( hth2 );
    CloseHandle( hth3 );

    printf("Primary thread terminating.\n");
}

Posted by gsi
:

여러개의 객체를 생성하는 코드를 아래와 같이 일반적으로 사용합니다.

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;

객체를 레지스트리에 등록하고
사용하게 됩니다.

이때 더 나아간다면 템플릿을 사용해서 예외 처리를 조금더 기능적으로 해줄 수 있습니다.

Posted by gsi
:

[C++] 타일 만들기..

C++ 2008. 5. 15. 02:04 |

데모로 만들어 보고 있는 타일 생성기 초안입니다.

타일을 하나하나 마우스로 찍어서 하는건 너무 노가다라고 생각 되고,
스타 크래프트의 맵 에디터는 신의 경지고 ^^..

우선 아래와 같은 실행 파일을 하나 제작해 봤습니다.
사용자 삽입 이미지

기본 토대는 여러가지의 카테고리를 가진 기본 타일이 존재 하고 그 타일의 주변을 감싸는 테두리를 검색한 후에 그 검색한 테두리 부분을 부드럽게 연결해 주는 겁니다.

관련코드 :


.
Posted by gsi
:

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. 지적 및 조언 및 질문 해주시면 고맙겠습니다.

Posted by gsi
:

제목이 참 아리송 합니다. ^^
하지만 내용을 읽고 나면 이해가 될거예요..

이전의 포스트 를 우선 보시고 이 내용을 꼭 보셔야 합니다.

우선 이전의 내용을 한번 짚어 보면...

단위 전략을 통해서 객체 생성에 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. 지적 및 조언 및 질문 해주시면 고맙겠습니다.

Posted by gsi
: