336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

https://t.me/dailycpp

Posted by 역시인생한방
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

※ C++ 소스 코드 상에서 현재 윈도우가 32bit 버전인지 64bit 버전인지 구분하는 방법에 대한 자료는 검색해보면 금방 구할 수 있습니다. 근데 그 중 많은 자료들이 IsWow64Process 함수 하나만 가지고 64bit 여부를 판단하는 잘못된 방법을 사용하고 있더군요. 이 때문에 많은 분들이 헷갈려 하실것 같아 정확한 방법을 간단하게 정리해서 올려봅니다.



현재 실행중인 프로세스 및 윈도우의 비트(32/64bit) 버전 확인

  

프로그램을 구현하다보면 소스 코드 상에서 현재 실행중인 프로세스가 32bit 버전인지 64bit 버전인지, 또는 64bit 윈도우즈에서 동작중인 32bit 버전(WOW64)인지 구분해할 일이 생길 수 있습니다. 또한 현재 설치된 윈도우가 32bit 버전인지 64bit 버전인지 구분해야 하는 경우도 있구요. 이와 같은 기능은 생각보다 간단하게 구현할 수 있으며, 다음과 같이 기능에 따라 세 가지 함수로 나누어 보았습니다.

 

BOOL IsCurrentProcess64bit()
{
#if defined(_WIN64)
    return TRUE;
#else
    return FALSE;
#endif
}

BOOL IsCurrentProcessWow64()
{
    BOOL bIsWow64 = FALSE;
    typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
    LPFN_ISWOW64PROCESS fnIsWow64Process;

    fnIsWow64Process = (LPFN_ISWOW64PROCESS)
                                       GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
    if (!fnIsWow64Process)
        return FALSE;

    return fnIsWow64Process(GetCurrentProcess(), &bIsWow64) && bIsWow64;
}

BOOL Is64BitWindows()
{
    if (IsCurrentProcess64bit())
        return TRUE;

    return IsCurrentProcessWow64();
}

 

 

IsCurrentProcess64bit 함수는 현재 프로세스가 32bit 인지 64bit 인지 확인하는 함수입니다. 사실 어플리케이션이 32bit 인지 64bit 인지는 컴파일 타임에 이미 결정되는 것이기 때문에 함수로 만들 필요 없이 #if defined 매크로를 사용하여 처리해도 되지만 가독성을 위해 함수로 만들어 봤네요.

 

IsCurrentProcessWow64 함수는 현재 프로세스가 WOW64 환경에서 동작중인지 확인하는 함수입니다. WOW64란 "Windows on Windows 64-bit" 의 약자로 64bit 윈도우에서 32bit 어플리케이션이 동작하게 해주는 서브 시스템을 환경을 얘기합니다. 즉, 이 함수는 현재 윈도우가 64bit 이고 현재 프로세스가 32bit 일 경우에만 TRUE를 리턴하게 되며 내부적으로는 IsWow64Process API 함수를 이용하여 구현되어 있습니다.

 

Is64BitWindows 함수는 현재 설치된 윈도우가 32bit 인지 64bit 인지 확인하는 함수입니다. 구글링 하다보니 괜찮은 방법으로 구현된 소스가 있어서 비슷하게 적용해보았습니다. 일단 현재 프로세스가 64bit 일 경우에는 설치된 윈도우는 두 말 할 것도 없이 64bit 입니다. 64bit 어플리케이션은 64bit 윈도우에서만 실행되기 때문이죠. 그리고 현재 프로세스가 32bit 일 경우에는 IsCurrentProcessWow64 함수를 호출하여 판단하면 됩니다. 현재 프로세스가 32bit 이고 WOW64 환경에서 동작하고 있다면 현재 설치된 윈도우는 64bit 라는 결론을 얻을 수 있는거죠.



Reference

 

How to detect programmatically whether you are running on 64-bit Windows


출처 : http://devmachine.blog.me/198372301

'Programming > C / C++' 카테고리의 다른 글

C++ 텔레그램 채널  (0) 2020.02.10
int64_t 값 출력하기  (0) 2015.08.15
to read UTF-8 XML using TinyXML  (0) 2015.08.06
Named Pipe Server Using Overlapped I/O and Client  (0) 2015.06.12
Windows Named Pipe 구현 간단 정리  (0) 2015.06.11
Posted by 역시인생한방
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

Windows 에서는 __int64 타입의 변수를 플랫폼(x86/x64)에 관계없이 printf("%I64d", val) 형태로 출력할 수 있다.

Linux 에서는 플랫폼에 영향이 없이 처리할려면 고려할 사항들이 있다.


x86에서는 long long int 가 64비트 정수이다. 그러나 x64로 넘어가면 long int 가 64비트 정수로 사용된다.

이들 문제로 인해서, 많은 혼선이 발생하고 있다.


일단 정수 타입은 다음과 같이 쓰자.

  • int64_t 타입을 쓰는 걸 권장한다. 여기에는 #include <stdint.h> 가 필요하다.
  • printf() 로 출력하려고 할 때, "%lld" (x86 환경), "%ld" (x64 환경) 이지만, 직접 쓰지 않는다.
    빌드 플랫폼이 바뀔 때마다 이 부분에서 경고가 발생한다.
    대신에 PRId64, PRIu64 를 쓰자. 여기에는 #include <inttypes.h> 가 필요하다.

#include <stdio.h> #include <stdint.h> #define __STDC_FORMAT_MACROS #include <inttypes.h> // PRId64, PRIu64 int main(int argc, char* argv[]) { int64_t val = 60000000000LL; uint64_t val2 = 90000000000000LL; printf("val: %"PRId64"\n", val); printf("val2: %"PRIu64"\n", val2); return 0; }


참고:

출처 : http://netmaid.tistory.com/28

Posted by 역시인생한방
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

#include <iostream>

using namespace std;

#include <atlstr.h>


int main()

{

char* src = "한글"; // ANSI CP949

CA2W unicode1(src); // ANSI -> UNICODE


CW2A utf8(unicode1, CP_UTF8); // UNICODE -> UTF8

CA2W unicode2(utf8, CP_UTF8); // UTF8 -> UNICODE

CW2A ansi(unicode2); // UNICODE -> ANSI

CW2A ansi2(CA2W(utf8, CP_UTF8));


printf("%s\n", src);

printf("%s\n", unicode1);

printf("%s\n", utf8);

printf("%s\n", unicode2);

printf("%s\n", ansi);

printf("%s\n", ansi2);


return EXIT_SUCCESS;

}


use above


Posted by 역시인생한방
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

// NamedPipeServer.h

#pragma once


namespace EventType

{

enum EventType

{

Exit,

Connect,

Max

};

}


class NamedPipeServer

{

public:

NamedPipeServer(LPCWSTR name);

~NamedPipeServer();


BOOL Start();

VOID Stop();


private:

std::wstring m_strName;

std::wstring m_strBuffer;

BOOL m_isStopped;

HANDLE m_hEvent[EventType::Max];

};


// NamedPipeServer.cpp

#include "stdafx.h"

#include "NamedPipeServer.h"


NamedPipeServer::NamedPipeServer(LPCWSTR name) :

m_strName(name),

m_strBuffer(),

m_isStopped(FALSE)

{

}


NamedPipeServer::~NamedPipeServer()

{

for (int i = 0; i < EventType::Max; ++i)

CloseHandle(m_hEvent[i]);

}


BOOL NamedPipeServer::Start()

{

OVERLAPPED oOverlapped;

HANDLE hPipe = NULL;

WCHAR buffer[MAX_BUFFER_SIZE] = { 0, };

DWORD dwRead;


while (m_isStopped == FALSE)

{

hPipe = CreateNamedPipe(m_strName.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, MAX_BUFFER_SIZE * sizeof(WCHAR), MAX_BUFFER_SIZE * sizeof(WCHAR), 0, NULL);

if (hPipe == INVALID_HANDLE_VALUE)

return FALSE;


for (int i = 0; i < EventType::Max; ++i)

{

m_hEvent[i] = CreateEvent(NULL, TRUE, FALSE, NULL);

if (m_hEvent[i] == NULL)

return FALSE;

}


ZeroMemory(&oOverlapped, sizeof(OVERLAPPED));

oOverlapped.hEvent = m_hEvent[EventType::Connect];


if (ConnectNamedPipe(hPipe, &oOverlapped) == 0)

{

if (GetLastError() != ERROR_IO_PENDING)

{

CloseHandle(hPipe);

return FALSE;

}

}


DWORD dwEvent = WaitForMultipleObjects(EventType::Max, m_hEvent, FALSE, INFINITE);


switch (dwEvent)

{

case EventType::Exit:

break;


case EventType::Connect:

ReadFile(hPipe, buffer, MAX_BUFFER_SIZE * sizeof(WCHAR), &dwRead, NULL);

if (0 < dwRead)

m_strBuffer = buffer;

break;

}


for (int i = 0; i < EventType::Max; ++i)

CloseHandle(m_hEvent[i]);


DisconnectNamedPipe(hPipe);

CloseHandle(hPipe);

}


return TRUE;

}


VOID NamedPipeServer::Stop()

{

m_isStopped = TRUE;


SetEvent(m_hEvent[EventType::Exit]);

}


// NamedPipeClient.h

#pragma once


class NamedPipeClient

{

public:

NamedPipeClient();

~NamedPipeClient();


BOOL Open(LPCWSTR name);

BOOL Send(LPCWSTR message);


private:

HANDLE m_hPipe;

};


// NamedPipeClient.cpp

#include "stdafx.h"

#include "NamedPipeClient.h"


NamedPipeClient::NamedPipeClient() : m_hPipe(NULL)

{

}


NamedPipeClient::~NamedPipeClient()

{

CloseHandle(m_hPipe);

}


BOOL NamedPipeClient::Open(LPCWSTR name)

{

SetLastError(ERROR_SUCCESS);


// Try to open a named pipe; wait for it, if necessary.

for (int retry = 0; retry < 3; ++retry)

{

m_hPipe = CreateFile(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);


// Break if the pipe handle is valid.

if (m_hPipe != INVALID_HANDLE_VALUE)

break;


// Exit if an error other than ERROR_PIPE_BUSY occurs. 

if (GetLastError() != ERROR_PIPE_BUSY)

return FALSE;


// All pipe instances are busy, so wait for 180 seconds.

if (WaitNamedPipe(name, 180000) == FALSE)

return FALSE;

}


return TRUE;

}


BOOL NamedPipeClient::Send(LPCWSTR message)

{

DWORD dwNumberOfBytesToWrite = (wcslen(message) + 1) * sizeof(WCHAR);

DWORD dwNumberOfBytesWritten;


return WriteFile(m_hPipe, message, dwNumberOfBytesToWrite, &dwNumberOfBytesWritten, NULL);

}


// main.cpp - Server

int main()

{

NamedPipeServer* pServer = new NamedPipeServer(L"\\\\.\\pipe\\PMAPipe");

std::thread pipe([=]

{

pServer->Start();

});


// for stop test

Sleep(300000);


pServer->Stop();


pipe.join();


if (pServer != NULL)

{

delete pServer;

pServer = NULL;

}


return EXIT_SUCCESS;

}


// main.cpp - Client

int main()

{

NamedPipeClient* pClient = new NamedPipeClient();

pClient->Open(L"\\\\.\\pipe\\PMAPipe");

pClient->Send(L"FUCK THAT SHIT");


if (pClient != NULL)

{

delete pClient;

pClient = NULL;

}


return 0;

}



'Programming > C / C++' 카테고리의 다른 글

int64_t 값 출력하기  (0) 2015.08.15
to read UTF-8 XML using TinyXML  (0) 2015.08.06
Windows Named Pipe 구현 간단 정리  (0) 2015.06.11
GetSystemMetrics  (0) 2015.06.04
Singleton  (0) 2015.06.02
Posted by 역시인생한방
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

Windows에서 Named Pipe를 실제로 사용하기 위해서는 Read/Write를 비동기적으로 처리해야 하는데, 이를 위해 흔히 사용되는 방법은 Overlapped I/O와 Event를 기반으로 하는 것입니다.

 

다시 말해서, 파이프를 생성하고 초기화한 후 파이프의 핸들과 함께 ReadFile/WriteFile API를 호출시, 무조건 데이터를 가지고 리턴하는게 아니라 호출 순간에 처리가 완료되었든 그렇지 않든 관계없이 바로 다음 줄로 넘어 가버립니다. (=비동기적) 그러면 당연히 개발자 입장에선 Read, Write가 완료되는 시점이 궁금하겠죠. 이 시점을 알아내는 방식이 여러가지가 있는데, 명명된 파이프 입출력 자체가 윈도의 Overlapped I/O에 해당되기 때문에 Overlapped I/O와 연계가 가능한 Event Object를 쓰겠다는 의미입니다.

 

ReadFile/WriteFile API의 arguments를 보면 Overlapped I/O를 위한 OVERLAPPED 구조체를 입력받는 항목이 있는데, OVERLAPPED 구조체의 내부에는 Event Object를 입력받는 변수가 있습니다. 이 변수에 CreateEvent를 이용해서 생성한 Event Object의 HANDLE을 넘기면 ReadFile/WriteFile이 완료되는 순간 해당 Event Object가 호출되는 것이죠. (참고로, ReadFile/WriteFile의 리턴값이 TRUE일 경우에는 처리해야 될 데이터의 양이 적어 즉시 끝난 것을 의미하지만, lpNumberOfBytes... 인수에 NULL을 준 경우에 한해 Event Object가 다시 한번 호출됩니다. 문제는 이 부분에 대한 MSDN의 설명이 완전히 막장 그 자체입니다 (...) 모순이 여기저기서 발생하는데, 제가 보기엔 TRUE인 경우에도 MSDN의 설명과 각종 예제 코드와는 달리 Pending으로 처리해야 하지 않나 싶습니다.)

 

그렇다면 구체적으로 어떻게 구현해야 할지가 문제가 되는데, MSDN에 Overlapped I/O와 Event를 결합하여 Pipe Server를 구현한 예제가 있습니다. n개의 Pipe에 대해 n개의 Event를 생성한 후, WaitForMultipleObjects에 이벤트의 배열을 넣고 무한루프를 돌리는 방식입니다. 하지만 이 예제에는 몇 가지 부족한 점이 있는데, 그중 한 가지는 Event가 하나만 존재하여 Connect/Read/Write 중 어느 사건에 대한 이벤트가 발생한 것인지 구분이 불가능하기 때문에 Connect->Read->Write->Close의 고정된 순서로 구현되어 원하는 임의의 시점에 실시간으로 Read / Write를 할 수가 없다는 것이고, 나머지 하나는 파이프를 위한 별도의 Thread가 존재하지 않아 전제 프로그램의 로직이 파이프의 실행만을 위해 사용되어야 한다는 것입니다. 그리고 댓글에서 제기된 문제점 하나를 추가하자면 Pipe와 관련된 데이터를 담는 struct가 초기화되지 않아서 일부 시스템에서 실행이 안 된다고 하는데, 0으로 초기화하면 해결됩니다.

참고로, MSDN의 예제에 있는 ReadFile / WriteFile에는 lpNumberOfBytesRead / lpNumberOfBytesWritten을 받게 되어있는데, 지속적으로 입출력을 할 경우엔 이렇게 하면 안되고 NULL을 준 다음 GetOverlappedResult에서 받아야 합니다. 이 부분에 대한 자세한 사항은 ReadFile / WriteFile의 MSDN에 일부 언급되어 있습니다.

 

따라서 실제 구현에서는 별도의 Thread를 생성하고 해당 Thread 안에서 Events에 대해 WaitForMultipleObjects를 실행하되, 파이프당 Connect, Read, Write, Update 총 4개의 Event를 등록하여 실시간으로 클라이언트와 데이터를 주고받을 수 있게 하면 됩니다. 이를 위해서는 Connect/Read/Write를 위한 3개의 OVERLAPPED 구조체와, 임의의 시점에 파이프에 뭔가를 쓰기 위해 WaitForMultipleObjects를 리턴시키는 Update Event를 등록하여, ConnectNamedPipe API에 Connect OVERLAPPED 객체를 등록하고, ReadFile과 WriteFile에 각각 Read/Write OVERLAPPED 객체를 등록하여 사용하게 됩니다. (Event가 3개로 늘어났기 때문에 Pending인지 나타내는 변수도 1개인 MSDN의 예제와 달리 Connect, Read, Write의 3개가  필요합니다.)

 

그리고 Read/Write를 위한 두개의 별도의 큐를 생성하고, Pipe server thread와 Main thread간의 동기화를 위한 두 개의 Critical Section Object 또한 생성하여 연계시킨 뒤, Pipe에서 Read Event가 발생시 Main thread를 위해 Read queue에 데이터를 읽어들인 이후 PostThreadMessage 등을 이용해 전달하고, 반대로 Main thread에서 파이프에 데이터를 쓸 경우 Write queue에 데이터를 넣은 다음 Update Event를 호출하는 식으로 하면 실시간 처리가 가능합니다. 이외에 큐를 초기화하기 위한 Reset Event 같은 것을 하나 더 등록해도 되겠죠.

 

아, 그리고 마지막으로 주의해야 할 것은 Pipe가 놀면 안된다는 겁니다. 일단 파이프가 연결된 상태가 됐을 경우, 항상 ReadFile로 읽고 있지 않으면 상대쪽 파이프가 끊겼을 때 WaitForMultipleObjects가 리턴하지 못하고 데드락이 걸리게 됩니다.

 

요약하자면, 위와 같이 구현했을 때 Main thread 입장에서 Pipe class를 보면 Init, Read, Write, Reset 정도의 메소드가 노출되고, 파이프의 읽기 이벤트는 PostThreadMessage로 전달받게 됩니다. 그리고 Pipe thread에서는 [이벤트 수신 대기 (WaitForMultipleObjects) -> 비동기 처리 결과 확인 (GetOverlappedResult) -> ReadFile/WriteFile 호출 및 큐 관련 처리] 정도로 무한루프를 돌게 되겠죠.

이상은 Named Pipe Server에 대한 설명이고, 클라이언트의 경우도 크게 다르지 않습니다.

 

(Pipe Server의 규모가 클 경우엔, Event 대신에 대규모 비동기 입출력에 높은 효율을 발휘하는 IOCP를 이용하는 방법도 있습니다.)


출처 : http://jscript.blog.me/220134379022

'Programming > C / C++' 카테고리의 다른 글

to read UTF-8 XML using TinyXML  (0) 2015.08.06
Named Pipe Server Using Overlapped I/O and Client  (0) 2015.06.12
GetSystemMetrics  (0) 2015.06.04
Singleton  (0) 2015.06.02
URLDownloadToFile example  (0) 2015.05.26
Posted by 역시인생한방
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

CreateWindow를 할때 보통 많이 쓰는 윈도우 스타일은 WS_OVERLAPPEDWINDOW이며 여기에는 크기 조정용 WS_THICKFRAME이 포함되어있기때문에 실 작업영역은 (설정한 윈도우 사이즈 - 프레임 사이즈) 만큼 감소되서 나온다.

그럼 온전한 작업영역을 갖기 위해서 어떻게 해야하나...

그거야 당연하지 않은가? 모자란 작업영역을 더해주면 된다!

수치를 일일히 재가지고 더해줘도 되긴하나 규격이랑 Set이랑 세트인 Get은 폼으로 있는게 아니다.

규격이 달라지면 수치를 일일히 잰게 달라질테고 그렇다고 규격마다 #ifdef를 일일히 걸어줄수도 없는 노릇이지 않은가?

이 문제를 해결하기 위해 GetSystemMetrics라는 함수에 대해 알아보기로 한거다.



우선 위의 사이트에 들어가서 무슨 인수가 있는지 쭉 살펴보자.

그럼 위의 인수들 중에서 중요하다고 생각되는것들을 뽑아서 설명하겠다.

1. 작업영역 확보를 위해.
윈도우 창에서 사이즈 오류가 나는 이유는 크게 타이틀바랑 프레임이다.

1-1)  WS_OVERLAPPED / WS_CAPTION
이 둘은 타이틀 바를 가지고 윈도우를 생성하게 해준다.
어찌보면 가장 기본적인 요소.
즉, 이 둘을 포함하고 있을때는 타이틀바 만큼의 사이즈를 포함해줘야한다.
SM_CYCAPTION 를 인수로 높이에 더해주면 된다.

1-2) WS_THICKFRAME / WS_SIZEBOX
사실 두개는 같은 인수.
크기 조절을 할수 있게 해주는 프레임 영역을 생성하여 크기 조절이 가능한 윈도우 창을 만들어준다.
SM_CXSIZEFRAME : 프레임의 가로 길이
SM_CYSIZEFRAME : 프레임의 세로 길이
당연히 CX는 넓이에 CY는 높이에 추가해야되며 양옆으로 프레임바가 있으니 * 2를 해줘야한다.
WS_THICKFRAME을 쓰지 않을때에는
SM_CXBORDER, SM_CYBORDER 를 이용한다

2. 화면을 정 중앙에 설정.
1을 선행한 이유는 2를 시행하기 위해 1이 필요하기 때문이다.
SM_CXSCREEN : 자신의 컴퓨터의 주 모니터의 가로 넓이를 뜻한다.
SM_CYSCREEN : 자신의 컴퓨터의 주 모니터의 세로 높이를 뜻한다.
윈도우 좌표계는 왼쪽위를 기준으로 잡는다.
즉, 윈도우가 중앙으로 오게 할려면 단순히 스크린 중앙을 맞추는게 아니라 거기에 윈도우창의 반만큼 더 빼줄 필요가 있다는거다.
최종적인 공식으로 (모니터/2) - (윈도우/2)를 사용하면 화면 중앙에 오게되는것.


'Programming > C / C++' 카테고리의 다른 글

Named Pipe Server Using Overlapped I/O and Client  (0) 2015.06.12
Windows Named Pipe 구현 간단 정리  (0) 2015.06.11
Singleton  (0) 2015.06.02
URLDownloadToFile example  (0) 2015.05.26
GetCurrentDirectory() vs GetModuleFileName()  (0) 2015.05.22
Posted by 역시인생한방
,

Singleton

Programming/C / C++ 2015. 6. 2. 17:54
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

C++에서 Singleton의 구현은 몇가지 방법이 있다.

이중 내가 주로 사용하는 방법은 아래의 방법이다.

 

class A

{

private:

  static A* m_Instance;

public:

  static A* Instance()

  {

    if( m_instance == NULL )

    {

      m_Instance = new A();
    }

    return m_Instance;

  }

};

 

위와 같이 구현된 형태가 내가 사용하는 Singleton의 기본 형태이다.

 

여기에 몇가지 추가 기능이 들어간다.

 

첫번째는 thread-safe를 위해서 Lock을 사용하는 것이다.

m_Instance가 여러 쓰레드에서 접근하기 때문에 두개 이상 생기는 것을 방지하기 위함이다.

그렇지만 매번 NULL 체크를 할 때마다 Lock을 잡는 것은 성능상 큰 문제가 있다.

그래서 사용하는 것이 double checked lock 패턴이다.

구현은 아래와 같다.

 

inline A* A::Instance()

{

  if( m_Instance == NULL )        // #1

  {

    m_Lock.Aquire();

    if( m_Instance == NULL )      // #2

    {

      m_Instance = new A();
    }

    m_Lock.Release();
  }

  return m_Instance;
}

이렇게 사용하려면 m_Instance는 아래와 같이 volatile로 선언이 바뀌어야 한다.

 

A * volatile A::m_Instance;

 

이것이 안들어가게 되면 Complier optimization에 의해서 #1에서 읽은 값과 #2에서 읽은 값이 항상 같게 되어서 Lock을 잡는 의미가 없어질 가능성이 있다.

당연히도 volatile을 쓰면 안정적인 동작이 되지만 성능은 떨어지게 된다.

Singleton은 Instance() 콜이 매우 잦을 가능성이 많기 때문에 성능이 저하되는 것은 안될 일이다.

이것을 해결하기 위해서 추가적인 함수를 사용해서 volatile을 제거한다.

 

class A

{

...

  static A* CreateInstance();

...
};

 

inline A* A::Instance()

{

  if( m_Instance != NULL )

  {

    return m_Instance;        // #3

  }
  return CreateInstance();
}

 

//-------------

// in .cpp

A* A::CreateInstance()

{

  m_Lock.Aquire();

  if( m_Instance == NULL )

  {

    m_Instance = new A();     // #4

  }

  m_Lock.Release();

  return m_Instance;
}

 

이렇게 inline이 아닌 함수로 생성을 위임하는 것을 통해서 volatile을 제거할 수 있다.

inline이 아니기 때문에 CreatInstance() 안에서 m_Instance를 새로 읽어올 것을 보장할 수 있다.

(물론 특정한 컴파일 옵션에 의해서 강제로 인라인 화 될 경우 어떻게 될지는 모르나 그렇게 까지 쓸경우는 없을 것이다.)

 

위의 예에서 Instance() 함수는 처음 생성될 때를 제외하고는 if 조건 하나를 조사하고 #3이 불리는 형태로 진행이 된다.

이것은 인라인화가 될 것이고 CPU의 branch prediction까지 대부분을 적중 시킬 것이다.

그렇기 때문에 실제 A::Instance()를 콜하면 수행되는 코드는 m_Instance를 직접 참조하는 것과 거의 차이가 나지 않을 것이다.

 

마지막으로 추가되는 기능은 프로그램 종료시 메모리를 정리하는 부분이다.

즉 new A() 로 생성된 m_Instance를 지워야 한다.

이것은 stdlib에 존재하는 atexit() 함수를 이용해서 구현하면 된다.

#4의 부분에 atexit()로 자신의 instance를 삭제하는 함수를 등록한다.

 

class A

{

...

private:

  static void  __cdecl DestoryInstance();

...
};

 

void __cdecl A::DestoryInstance()

{

  delete m_Instance;
}

#4 다음 줄에 아래 추가

atexit(&DestoryInstance);

 

이렇게 되면 프로그램 종료직전에 DestoryInstance() 가 불려서 할당된 객체를 삭제하게 된다.

[출처] Singleton|작성자 솔라리스



출처 : http://kimsk99.blog.me/50007325992

참고 : http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

'Programming > C / C++' 카테고리의 다른 글

Windows Named Pipe 구현 간단 정리  (0) 2015.06.11
GetSystemMetrics  (0) 2015.06.04
URLDownloadToFile example  (0) 2015.05.26
GetCurrentDirectory() vs GetModuleFileName()  (0) 2015.05.22
빠른 음수화  (0) 2015.02.07
Posted by 역시인생한방
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

#pragma comment(lib, "urlmon.lib")

#include <urlmon.h>


BOOL Download(LPCWSTR url, LPCWSTR filename)

{

DeleteUrlCacheEntry(url);


HRESULT result = URLDownloadToFile(NULL, url, filename, 0, NULL);

if (result == S_OK)

{

return TRUE;

}

else if (result == E_OUTOFMEMORY)

{

}

else if (result == INET_E_DOWNLOAD_FAILURE)

{

}


return FALSE;

}


DeleteUrlCacheEntry() 함수로 해당 파일에 대한 캐쉬를 지워주고 사용하면

매번 새로운 화일을 다운로드 받을 수 있습니다.


참고 : https://msdn.microsoft.com/en-us/library/ms775123(v=vs.85).aspx

http://blog.naver.com/kkt3212/100023057921

'Programming > C / C++' 카테고리의 다른 글

GetSystemMetrics  (0) 2015.06.04
Singleton  (0) 2015.06.02
GetCurrentDirectory() vs GetModuleFileName()  (0) 2015.05.22
빠른 음수화  (0) 2015.02.07
This program might not have installed correctly  (0) 2015.02.07
Posted by 역시인생한방
,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

GetCurrentDirectory() 함수는 현재 폴더를 가져오고


GetModuleFileName() 함수는 실행파일이 있는 폴더의 위치를 가져온다는 차이가 있다.

'Programming > C / C++' 카테고리의 다른 글

Singleton  (0) 2015.06.02
URLDownloadToFile example  (0) 2015.05.26
빠른 음수화  (0) 2015.02.07
This program might not have installed correctly  (0) 2015.02.07
문자열 _T("")와 L""  (0) 2015.02.07
Posted by 역시인생한방
,