Features

Highlights:

  • Open Source! GPLv3, no hidden costs.
  • Cross-platform. Runs on Linux, Mac, Windows (uses wxWidgets).
  • Written in C++. No interpreted languages or proprietary libs needed.
  • Extensible through plugins

 

Compiler:

  • Multiple compiler support:
    • GCC (MingW / GNU GCC)
    • MSVC++
    • clang
    • Digital Mars
    • Borland C++ 5.5
    • Open Watcom
    • ...and more
  • Very fast custom build system (no makefiles needed)
  • Support for parallel builds (utilizing your CPU's extra cores)
  • Multi-target projects
  • Workspaces to combine multiple projects
  • Inter-project dependencies inside workspace
  • Imports MSVC projects and workspaces (NOTE: assembly code not supported yet)
  • Imports Dev-C++ projects

 

Debugger:

  • Interfaces GNU GDB
  • Also supports MS CDB (not fully featured)
  • Full breakpoints support:
    • Code breakpoints
    • Data breakpoints (read, write and read/write)
    • Breakpoint conditions (break only when an expression is true)
    • Breakpoint ignore counts (break only after certain number of hits)
  • Display local function symbols and arguments
  • User-defined watches (support for watching user-defined types through scripting)
  • Call stack
  • Disassembly
  • Custom memory dump
  • Switch between threads
  • View CPU registers

 

Interface:

  • Syntax highlighting, customizable and extensible
  • Code folding for C, C++, Fortran, XML and many more files.
  • Tabbed interface
  • Code completion
  • Class Browser
  • Smart indent
  • One-key swap between .h and .c/.cpp files
  • Open files list for quick switching between files (optional)
  • External customizable "Tools"
  • To-do list management with different users

And many more features provided through plugins!

 

출처: <http://www.codeblocks.org/features>

 

Windows, linux, mac os x 플랫폼을 지원하고, 대다수의 메이저 컴파일러를 지원합니다. 하지만 gcc 사용하지 않을거라면 굳이 무료 버전도 배포중인 MSVC 사용하지 않을 이유가 없기에… GCC용으로 사용하시길 권장합니다.

 

기존 MSVC Dev-C++ project 파일도 import 되기 때문에 컨버팅 비용도 상대적으로 적습니다. 물론 platform independency 코드를 작성하지 못한 코드는 모두 작성 해야겠지만요.

 

Code::blocks download

http://www.codeblocks.org/downloads/26

 

GCC에서 boost 사용하게 되면 platform independency 코드를 작성할 있게 됩니다.

아래 링크는 code::blocks boost 빌드하는 방법입니다.

 

Code::blocks for boost

http://stackoverflow.com/questions/13301089/how-do-i-build-boost-libraries-on-codeblocks-windows



Posted by 엘키 엘키

댓글을 달아 주세요

이 글은, c++을 기반으로 작성되었지만 예외 처리 기능 (try-catch, try-except 등)을 가진 모든 언어에 적용 되는 내용입니다. 파일에서 데이터를 읽는 코드를 작성해봅시다. c++로 작성해보겠습니다.
int _tmain(int argc, _TCHAR* argv[])
{
	FILE* fp;
	fp = fopen("test.bin", "rb");

	if(fp == NULL)
	{
		// 예외 처리
		return 1;
	}

	char buffer[512] = {0, };
	fread(buffer, 512, 1, fp);
	return 0;
}
대부분 위 코드처럼 작성하게 됩니다. 위 코드에서 누락된 예외처리 발견하신분? 예. 바로 fread 부분입니다. 

항목이 읽히지 않았을 때, fread는 0을 리턴하고, 0을 리턴했을 때에는 로그를 찍는다거나 등의 예외처리를 해주어야 파일을 잘못 읽었는지를 조기에 발견 할 수 있습니다. 

헌데 대부분의 수많은 예제는 첫 예제처럼 fread에 대한 검사는 이루어지지 않습니다. 바로 이 부분이 쉽게 놓치는 첫 감염입니다. 

감염을 조기에 발견하지 못하면, 숙주를 찾기 어려워지고 숙주를 찾지 못하면 전염병처럼 퍼지게 됩니다. 위 코드의 모범적 예외 처리 방식은 아래와 같습니다.
int _tmain(int argc, _TCHAR* argv[])
{
	FILE* fp;
	fp = fopen("test.bin", "rb");

	if(fp == NULL)
	{
		// 예외 처리
		return 1;
	}

	char buffer[512] = {0, };
	int read_count = fread(buffer, 512, 1, fp);
	if(read_count == 0)
	{
		// 예외 처리
		return 2;
	}
	return 0;
}
이렇게 되어야 buffer의 데이터가 유효하게 로직으로 전달되었는지 검사할 수 있습니다. 

혹자는, 컨텐츠 코드에서 방어적으로 데이터가 유효한지 검증해야 되는게 아니냐라고 하실 수도 있습니다. 

하지만, 그 당연하다 여겨지는 전치검사/후치보장은 막상 그렇게 당연하게 여겨지지 않는게 현실입니다. 

심지어 클래스 내부 멤버에 대해선 전치검사 대상으로 여겨지지 않는 경우가 태반입니다. 게다가 작성하는 API 함수 및 프로젝트에서 구현한 함수마다 사용법및 예외 처리 방식을 검토하는 일은 매우 번거롭고 실수가 있을 수 밖에 없죠. 

같은 프로젝트내에 리턴값 및 예외 처리 방식이 조금만 다르게 작성 (WIN32 API에서의 DWORD형의 결과값과 COM에서 HRESULT 값이 주로 그런 케이스. 어떤 매크로로 검사해야될지부터 혼동이 옴.) 된 걸로도 충분히 혼란스러운데, 여러개의 서드파티 솔루션까지 사용하는 규모가 큰 프로젝트의 경우 그 피로도가 장난이 아니죠. 

 그 대안으로 대부분의 언어에서는 예외 처리 기능을 지원합니다. c++의 경우 try-catch(try-except는 SEH)가 그 역할을 하는데요, boost나 STL의 경우에는 내부적으로 오류를 감지하면 throw로 예외를 던지고 있습니다. 말로만 하면 감이 안오실테니 코드로 보여드리겠습니다.
int _tmain(int argc, _TCHAR* argv[])
{
	try
	{
		FILE* fp;
		fp = fopen("test.bin", "rb");

		if(fp == NULL)
		{
			throw std::exception("file open failed.");
		}

		char buffer[512] = {0, };
		int read_count = fread(buffer, 512, 1, fp);
		if(read_count == 0)
		{
			throw std::exception("file data read failed.");
		}
	}
	catch(const std::exception& e)
	{
		std::cout << e.what() << std::endl;
		return 1;
	}
	return 0;
}
사용자 정의 exception 클래스를 이용할 수도 있지만, 여기선 쉬운 예제를 위해 std::exception 클래스를 통해 예외처리를 했습니다. 사실 이렇게 간단한 예제로는 예외처리가 뭐가 더 낫다는건지 감이 잘 오지 않습니다. 저도 그랬으니까요. 자~ 코드가 복잡해집니다. 다양한 기능이 붙고, 추상화니, 메시지 핸들러니 각종 쌈박한 로직을 구현해두었습니다. 만들다보니 기능이 붙고 붙네요. 외부 라이브러리도 붙었습니다. 정규식은 boost쓰고, 컨테이너는 stl 쓰고, json은 json spirit, xml은 tinyxml 을 붙였군요. 제가 이 글을 쓰고자 했던건 사실 tinyxml 때문이었습니다. tinyxml 물론 잘 만들어진 라이브러리입니다. 저도 아주 잘 쓰고 있고요. 헌데 이 tinyxml에서 문제가 하나 있었습니다.
TiXmlElement* pNickname = pPlayerElem->FirstChildElement("nickname");
if (pNickname  == NULL)
	return false;

std::string nickname = pNickname >GetText();
문제가 된 부분이 보이시나요? 전 몇번이나 코드를 훓어보던 중에야 원인을 찾을 수 있었습니다. 

원인은 pNickname >GetText() 이 함수가 문제였습니다.

위 코드는 nickname을 문자열형으로 읽어내는 코드입니다. 위 코드는 정상적으로 데이터가 포함되어있을 땐(<Nickname>엘키</Nickname>) 문제가 없습니다.   헌데 만약 이렇게 비어있는 값(<Nickname></Nickname>)일 때 읽는다면? 문제가 생겼습니다. 


비어있을 때 문제가 생긴다는 것도, 코드를 자세히 들여다 보고 나서야 확인한 것이지, 덤프를 본 직후에 바로 알아볼 순 없었어요. 


한 줄 한 줄 코드를 검토하며 함수 내부를 들여다 본 순간… 아차 싶었습니다.

const char* TiXmlElement::GetText() const
{
	const TiXmlNode* child = this->FirstChild();
	if ( child ) {
		const TiXmlText* childText = child->ToText();
		if ( childText ) {
			return childText->Value();
		}
	}
	return 0;
}
child->ToText() 함수가는 내부에 포함된 데이터가 비어있을 때 NULL이 반환되고, 이어 함수 최하단에서 return 0; 코드를 타 NULL 포인터가 반환되었습니다. 

사실 이 함수가 반환타입이 const char* 인 만큼 return ""; 으로 빈 문자열을 반환할 줄 알았습니다.  하지만 빈문자열과 child가 없는 등의 Text화 실패에 대한 상황을 구분하기 위해 return 0; 으로 처리하고 있더군요. API를 꼼꼼하게 체크해보지 못한 개발자의 잘못도 있지만 구현상의 애매모호함도 엄연히 존재한다고 봅니다. 

1. 오류 상황과 데이터가 없음을 모두 return 0; 즉 NULL 포인터로 반환하고 있는 문제
2. const char*형은 문자열형만 반환될뿐 사실은 pointer의 개념보다는 문자열 첫 주소가 반환된다는 의미로도 인식되어, NULL 포인터가 반환될 거라 인식하기 어려운 문제를 가지고 있는것이죠.

결과적으로 GetText() 함수의 구현 코드를 보니 NULL 포인터가 반환되는지 검사해주어야 했습니다.

위 코드를 예외처리해봅시다.
TiXmlElement* pNickname = pPlayerElem->FirstChildElement("nickname");
if (pNickname  == NULL)
	return false;

if(pNickname->GetText() == NULL)
	return false;

std::string nickname = pNickname >GetText();

TiXmlElement* pUserID = pPlayerElem->FirstChildElement("user_id");
if (pUserID  == NULL)
	return false;

if(pUserID >GetText() == NULL)
	return false;

std::string user_id= pUserID>GetText();
이렇게 해주어야합니다. GetText를 사용하는 코드가 많으면 많을수록 if문은 늘어납니다. 물론 위의 if문을
if (pUserID  == NULL || pUserID >GetText() == NULL)
	return false;
or 연산으로 수정할수야 있겠지만, 그렇다손쳐도 GetText() 하는 함수마다 체크해주어야 함은 변함이 없습니다.
const char* TiXmlElement::GetText() const
{
	const TiXmlNode* child = this->FirstChild();
	if ( child == NULL) 
	{
		throw std::exception("TiXmlElement::GetText() FirstChild is NULL");
	}

	const TiXmlText* childText = child->ToText();
	if ( childText == NULL) 
	{
		throw std::exception("TiXmlElement::GetText() ToText Failed.");
	}
	return childText->Value();
}
이렇게 코드를 작성했다면 어떨까요?
try
{
	TiXmlElement* pNickname = pPlayerElem->FirstChildElement("nickname");
	if (pNickname  == NULL)
		continue;

	std::string nickname = pNickname >GetText();

	TiXmlElement* pUserID = pPlayerElem->FirstChildElement("user_id");
	if (pUserID  == NULL)
		return false;

	std::string user_id= pUserID>GetText();
}
catch(const std::exception& e)
{
	std::cout << e.what() << std::endl;
	// 일괄적인 예외 핸들링
	return false;
}

만약 try안에서 호출된 다른 함수에서 throw가 존재한다해도 코드의 흐름을 멈추고, 원인을 알아낼 수 있게 됩니다. 


이렇게 되면 예외처리를 일괄적으로 할 수 있고, 코드의 흐름을 중단하는 역할도 맡길 수가 있습니다. 


 보통 외부 라이브러리 함수에서 크리티컬한 상황이 발생했을 경우라거나, 유틸리티 함수등에서의 실패를 리턴값으로 반환하곤 하는데, 이보다 더 강하게 코드의 진행을 중지 시키고 싶을 때 (2차 감염을 막고, 현재 상황의 위험성을 알리기 위해서 주로 이렇게 하죠) 라고 볼 수 있습니다. 


리턴값은 그 함수를 사용하는 코드마다 체크 로직과 핸들링 로직을 구현해주어야 합니다. 


이에 비해 예외 처리는, 적절한 위치에 try-catch로 묶어주기만해도 throw로 던져진 예외를 핸들링 할수가 있게 됩니다. 


물론 모든 코드가 그렇게 예외처리를 하는 것은, 코드의 흐름을 원치 않는 곳에서 중단 시키기 때문에, 상황에 따라 국소적인 코드마다 try-catch 핸들링을 해주어야 하는 부작용도 있긴하지만, 순작용이 훨씬 많은 예외 처리 방식입니다. 

 심지어 이는 C++에 국한된 것이 아닌, 대다수의 언어에서 지원되는 예외처리 방식이므로 한번 몸에 익혀두시면 두고 두고 활용하시기에 좋습니다. 


C++도 표준 라이브러리들은 대부분 throw로 예외처리를 하기 때문에 try-catch로 예외처리를 할수있고, ruby등의 기타 스크립트 언어들도 내부 함수 예외를 try-catch로 핸들링 할 수 있습니다. 


try-catch 적용시 많이들 어렵게 생각하시는 점은, 어디서 부터 어디까지 try-catch로 감싸도 되는가에 대한 고민이라고 생각하는데요, 이는 boost나 stl등 내부적으로 throw를 사용하고 있는 함수들 부터 적용해보시면 어렵지 않게 적응하실 수 있습니다.


코드의 흐름을 제어하고, 일반화된 오류 처리를 도와주는 예외처리. 적극적으로 써보시면 어떨까요?




Posted by 엘키 엘키

댓글을 달아 주세요

http://msdn.microsoft.com/en-us/library/aa272889%28v=VS.60%29.aspx


Routine Mappings

Visual Studio 6.0
This topic has not yet been rated

The generic-text routine mappings are defined in TCHAR.H. _tccpy and _tclen map to functions in the MBCS model; they are mapped to macros or inline functions in the SBCS and Unicode models for completeness. For information on a generic text routine, see the help topic about the corresponding SBCS-, _MBCS-, or _UNICODE-related routine.

More specific information about individual routines listed in the left column below is not available in this documentation. However, you can easily look up the information on a corresponding SBCS-, _MBCS-, or _UNICODE-related routine. Use the Search command on the Help menu to look up any generic-text routine listed below.

For related information, see Generic-Text Mappings in TCHAR.H.

Generic-Text Routine Mappings

Generic-Text 
Routine Name
SBCS (_UNICODE & 
MBCS Not Defined)

_MBCS Defined

_UNICODE Defined
_fgettcfgetcfgetcfgetwc
_fgettcharfgetcharfgetchar_fgetwchar
_fgettsfgetsfgetsfgetws
_fputtcfputcfputcfputwc
_fputtcharfputcharfputchar_fputwchar
_fputtsfputsfputsfputws
_ftprintffprintffprintffwprintf
_ftscanffscanffscanffwscanf
_gettcgetcgetcgetwc
_gettchargetchargetchargetwchar
_gettsgetsgetsgetws
_istalnumisalnum_ismbcalnumiswalnum
_istalphaisalpha_ismbcalphaiswalpha
_istascii__isascii__isasciiiswascii
_istcntrliscntrliscntrliswcntrl
_istdigitisdigit_ismbcdigitiswdigit
_istgraphisgraph_ismbcgraphiswgraph
_istleadAlways returns false_ismbbleadAlways returns false
_istleadbyteAlways returns falseisleadbyteAlways returns false
_istlegalAlways returns true_ismbclegalAlways returns true
_istlowerislower_ismbcloweriswlower
_istprintisprint_ismbcprintiswprint
_istpunctispunct_ismbcpunctiswpunct
_istspaceisspace_ismbcspaceiswspace
_istupperisupper_ismbcupperiswupper
_istxdigitisxdigitisxdigitiswxdigit
_itot_itoa_itoa_itow
_ltot_ltoa_ltoa_ltow
_puttcputcputcputwc
_puttcharputcharputcharputwchar
_puttsputsputsputws
_tmainmainmainwmain
_sntprintf_snprintf_snprintf_snwprintf
_stprintfsprintfsprintfswprintf
_stscanfsscanfsscanfswscanf
_taccess_access_access_waccess
_tasctimeasctimeasctime_wasctime
_tccpyMaps to macro or inline function_mbccpyMaps to macro or inline function
_tchdir_chdir_chdir_wchdir
_tclenMaps to macro or inline function_mbclenMaps to macro or inline function
_tchmod_chmod_chmod_wchmod
_tcreat_creat_creat_wcreat
_tcscatstrcat_mbscatwcscat
_tcschrstrchr_mbschrwcschr
_tcsclenstrlen_mbslenwcslen
_tcscmpstrcmp_mbscmpwcscmp
_tcscollstrcoll_mbscollwcscoll
_tcscpystrcpy_mbscpywcscpy
_tcscspnstrcspn_mbscspnwcscspn
_tcsdec_strdec_mbsdec_wcsdec
_tcsdup_strdup_mbsdup_wcsdup
_tcsftimestrftimestrftimewcsftime
_tcsicmp_stricmp_mbsicmp_wcsicmp
_tcsicoll_stricoll_stricoll_wcsicoll
_tcsinc_strinc_mbsinc_wcsinc
_tcslenstrlenstrlenwcslen
_tcslwr_strlwr_mbslwr_wcslwr
_tcsnbcnt_strncnt_mbsnbcnt_wcnscnt
_tcsncatstrncat_mbsnbcatwcsncat
_tcsnccatstrncat_mbsncatwcsncat
_tcsncmpstrncmp_mbsnbcmpwcsncmp
_tcsnccmpstrncmp_mbsncmpwcsncmp
_tcsnccnt_strncnt_mbsnccnt_wcsncnt
_tcsnccpystrncpy_mbsncpywcsncpy
_tcsncicmp_strnicmp_mbsnicmp_wcsnicmp
_tcsncpystrncpy_mbsnbcpywcsncpy
_tcsncset_strnset_mbsnset_wcsnset
_tcsnextc_strnextc_mbsnextc_wcsnextc
_tcsnicmp_strnicmp_mbsnicmp_wcsnicmp
_tcsnicoll_strnicoll_strnicoll_wcsnicoll
_tcsninc_strninc_mbsninc_wcsninc
_tcsnccnt_strncnt_mbsnccnt_wcsncnt
_tcsnset_strnset_mbsnbset_wcsnset
_tcspbrkstrpbrk_mbspbrkwcspbrk
_tcsspnp_strspnp_mbsspnp_wcsspnp
_tcsrchrstrrchr_mbsrchrwcsrchr
_tcsrev_strrev_mbsrev_wcsrev
_tcsset_strset_mbsset_wcsset
_tcsspnstrspn_mbsspnwcsspn
_tcsstrstrstr_mbsstrwcsstr
_tcstodstrtodstrtodwcstod
_tcstokstrtok_mbstokwcstok
_tcstolstrtolstrtolwcstol
_tcstoulstrtoulstrtoulwcstoul
_tcsupr_strupr_mbsupr_wcsupr
_tcsxfrmstrxfrmstrxfrmwcsxfrm
_tctimectimectime_wctime
_texecl_execl_execl_wexecl
_texecle_execle_execle_wexecle
_texeclp_execlp_execlp_wexeclp
_texeclpe_execlpe_execlpe_wexeclpe
_texecv_execv_execv_wexecv
_texecve_execve_execve_wexecve
_texecvp_execvp_execvp_wexecvp
_texecvpe_execvpe_execvpe_wexecvpe
_tfdopen_fdopen_fdopen_wfdopen
_tfindfirst_findfirst_findfirst_wfindfirst
_tfindnext_findnext_findnext_wfindnext
_tfopenfopenfopen_wfopen
_tfreopenfreopenfreopen_wfreopen
_tfsopen_fsopen_fsopen_wfsopen
_tfullpath_fullpath_fullpath_wfullpath
_tgetcwd_getcwd_getcwd_wgetcwd
_tgetenvgetenvgetenv_wgetenv
_tmainmainmainwmain
_tmakepath_makepath_makepath_wmakepath
_tmkdir_mkdir_mkdir_wmkdir
_tmktemp_mktemp_mktemp_wmktemp
_tperrorperrorperror_wperror
_topen_open_open_wopen
_totlowertolower_mbctolowertowlower
_totuppertoupper_mbctouppertowupper
_tpopen_popen_popen_wpopen
_tprintfprintfprintfwprintf
_tremoveremoveremove_wremove
_trenamerenamerename_wrename
_trmdir_rmdir_rmdir_wrmdir
_tsearchenv_searchenv_searchenv_wsearchenv
_tscanfscanfscanfwscanf
_tsetlocalesetlocalesetlocale_wsetlocale
_tsopen_sopen_sopen_wsopen
_tspawnl_spawnl_spawnl_wspawnl
_tspawnle_spawnle_spawnle_wspawnle
_tspawnlp_spawnlp_spawnlp_wspawnlp
_tspawnlpe_spawnlpe_spawnlpe_wspawnlpe
_tspawnv_spawnv_spawnv_wspawnv
_tspawnve_spawnve_spawnve_wspawnve
_tspawnvp_spawnvp_spawnvp_tspawnvp
_tspawnvpe_spawnvpe_spawnvpe_tspawnvpe
_tsplitpath_splitpath_splitpath_wsplitpath
_tstat_stat_stat_wstat
_tstrdate_strdate_strdate_wstrdate
_tstrtime_strtime_strtime_wstrtime
_tsystemsystemsystem_wsystem
_ttempnam_tempnam_tempnam_wtempnam
_ttmpnamtmpnamtmpnam_wtmpnam
_ttoiatoiatoi_wtoi
_ttolatolatol_wtol
_tutime_utime_utime_wutime
_tWinMainWinMainWinMainwWinMain
_ultot_ultoa_ultoa_ultow
_ungettcungetcungetcungetwc
_vftprintfvfprintfvfprintfvfwprintf
_vsntprintf_vsnprintf_vsnprintf_vsnwprintf
_vstprintfvsprintfvsprintfvswprintf
_vtprintfvprintfvprintfvwprintf


Posted by 엘키 엘키

댓글을 달아 주세요

struct CHAR_COLLECTION_DATA
{
	int CharID;
	int Value;
	int ValueCode;
};

CHAR_COLLECTION_DATA CollectionData  = {m_CharID, m_Value, m_ValueCode };

이런 코드가 있었다.



기능을 추가 하시려다보니 습관적으로


struct CHAR_COLLECTION_DATA
{
	int CharID;
	int ClassID; // 다른 변수를 중간에 추가함.
	int Value;
	int ValueCode;
};

CHAR_COLLECTION_DATA CollectionData  = {m_CharID, m_Value, m_ValueCode };


같은 코드고 컴파일 오류도 없지만 원래 코드와 다르게, CharID, Value, ValueCode를 채우지 않고, CharID, ClassID, Value에만 값을 채우는 코드가 되어버렸다.



물론 사용 코드를 전부다 훓어보지 않은 문제가 있긴 하지만, 컴파일 오류로 강제되지 않은 초기화도 좋은 제약은 아니다.


struct CHAR_COLLECTION_DATA
{
	int CharID;
	int ClassID; // 다른 변수를 중간에 추가함.
	int Value;
	int ValueCode;

	CHAR_COLLECTION_DATA()
                : CharID(0)
		, ClassID(0)
		, Value(0)
		, ValueCode(0)
	{

	}


	CHAR_COLLECTION_DATA(int charID, int classID, int value, int valueCode)
		: CharID(charID)
		, ClassID(classID)
		, Value(value)
		, ValueCode(valueCode)
	{

	}

};


기존 코드가 이렇게 짜여져 있었다면, 기존 코드였던


CHAR_COLLECTION_DATA CollectionData(m_CharID, m_Value, m_ValueCode);


는 컴파일 오류를 일으킨다.


자연스레 


CHAR_COLLECTION_DATA CollectionData(m_CharID, m_ClassID, m_Value, m_ValueCode);


위와 같이 고치게 될 것이다.



물론 모든 상황에서 주의 깊게 코드를 찾아보고 고치면 얼마나 좋겠냐 만은, 커버리지는 높을수록 좋은 것.


컴파일 오류로 막을 수 있는 습관은 갖추는 게 좋다.



구조체 이니셜라이저는 자제 하자.

Posted by 엘키 엘키

댓글을 달아 주세요

Ten C++11 Features Every C++ Developer Should Use

http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer


위 article을 간략하게 요약해봤습니다.


1. Auto

Auto는 C++ 0x에서도 주요 Feature였죠.

컴파일 타임 타입 유추 기능입니다.

일반적으로 typedef 해서 자료형을 정의해두고, iterator, value_type 등을 사용해야 했던 번거로움을 한번에 날릴 수 있는 좋은 기능이죠.


2. nullptr

NULL을 대체하는 type 타입입니다.

NULL이 ((void*)0)이나 0으로 define 되어있는 점을 감안 했을 때, type 검사가 이루어질 수 있는 nullptr은 모호함을 제거하기에 적합하다고 보여지네요.


3. range-based for loops

for each와 같은 느낌의 범위 전체 순환 기능입니다.

for each ( obj in data_structure ) 와 동일한 기능으로 보여지네요.

추가적인 장점이라면 배열에도 사용 가능해졌다는 점입니다.


4. override and final

override는 가상함수를 override했음을 의미하고, 상위 클래스중에 overriding될 메소드가 없으면 에러를 뱉는 키워드인데요, 이 키워드가 표준화임을 의미하는군요.

final은 override를 더이상 할 수 없게 막는 키워드죠.


5. Strongly-typed enums

enum을 강화해줬네요. 기존의 enum은 상수 나열형으로 유용하긴하지만, 다른 타입들보다 사용시 어려운 점이 많아 힘들었다는 점을 감안 했을 때, 아주 강해졌다고 보여집니다.


6. Smart pointers

개념 및 life-cycle의 이해를 위해 unique_ptr (구 auto_ptr), shared_ptr (흔히 smart_ptr을 shared_ptr만을 지칭하는 걸로 오해하곤 하는데 아닙니다.), weak_ptr을 적절히 사용하면 코드 가독성을 높일 수 있습니다.

이 세가지 smart pointer 에 대한 설명이네요.


7. Lambdas

익명 함수의 유용성은 더 말해 입이 아플 정도겠죠?


8. non-member begin() and end()

반복자를 member외에 추가로 template method화 했다는 의미입니다.

일관성을 위해 변경했다고 하고... 배열에도 사용가능해졌다는 장점도 있다고 하네요.


9. static_assert

GPG 3권에서 나왔던 compile_assert와 유사한 기능이 표준화되었군요.

컴파일 타입 검사를 강화하는 기능입니다.


10. Move semantics

R-value reference의 효율을 의미하는 것으로, 동일한 객체의 리소스를 다른 객체로 전송해서, 복사 비용을 줄이는 매너니즘을 의미합니다.

실제로 이로 인한 성능 향상을 c++ 0x에서도 많이 봤었죠.

정확히 하자면 move semantics의 구현중 r-value reference가 있는 개념이라고 하네요.


이렇듯 C++이 진화하고 있습니다.

루비같은 언어와는 애초에 접근자체가 다르긴하지만, 자바나 기타 C계열 파생 언어등에 비해 갖고 있던 리스크를 지속적으로 해결하고 있는 걸 보면 뿌듯하네요.

서버 개발은 node.js 등으로 많이 옮겨가고 있지만, 아직 C++은 죽지 않았다고 생각합니다. (개인적으론 앞으로도 계속 유지될 언어라고 생각합니다. 왠만한 언어들보다 라이프 사이클이 더 길거라고 믿는 편이죠)


여기까지 C++ 11에 대한 간략한 정리였습니다. 감사합니다.


C++ 11 Wiki

http://ko.wikipedia.org/wiki/C%2B%2B11


Posted by 엘키 엘키
 TAG C++ 0x, C++ 11

댓글을 달아 주세요

제가 SmartPointer를 이해하고 사용하는 방식은 다음과 같습니다.


unique_ptr 

auto_ptr의 리네이밍 버전이죠. 포인터 소유권을 한 인스턴스에게 보장 시키기 위한 개념입니다. 소유자가 명확하기 때문에, native-ptr에서의 소유권 개념이 모호해질 수 있는 문제를 해결합니다.


shared_ptr 

smart_ptr의 리네이밍 버전이라 볼 수 있습니다. 

포인터 소유권을 나누어 갖기 때문에 native-ptr에서의 원본 포인터 소멸로 인한 참조 오류를 해결합니다. 

dangling-ptr이 되는 문제는 해결하지만, life-cycle이 명확해지지 않는 문제와 상호 참조 오류를 만들곤 합니다. 

물론 잘 사용하면 문제가 없지만요.


weak_ptr

소유권을 갖지 않는 ptr 입니다. 

shared_ptr에게서 포인터 주소를 얻어와도 참조횟수가 증가한다거나, 소유권을 얻어오지 않습니다.

shared_ptr을 좀 더 완전하게 만드는 데에 일조 했습니다. (shared_ptr이 소유권을 공유한다는 것을 의미한다면, weak_ptr은 소유권을 가지지 않는 포인터라는 의미를 명확히 해주기 떄문)




boost_any는 의미상 좀 스마트 포인터와는 다르지만 애용하고 있어서 꼽사리로 집어넣어보겠습니다.

http://www.boost.org/doc/libs/1_53_0/doc/html/any.html


any_ptr(http://elky.tistory.com/323)의 boost 버전이랄까요?


void*로 담으면 잘못된 캐스팅할 여지가 넘쳐나서, 안전한 캐스팅을 지원하는 (대입 연산자를 통해) 기능입니다.



이로써 간략하게나마 스마트 포인터들에 대해서 알아봤습니다.



참고 자료

Smart Pointers (Modern C++)

http://msdn.microsoft.com/ko-kr/library/hh279674.aspx

Posted by 엘키 엘키

댓글을 달아 주세요

윈도우 예외 처리에 대한 정리 개요 윈도우에서 사용가능한 예외 처리로는 C++ 예외 처리와, SEH (Structured Exception Handling) 이 있습니다.

일반적으로 SEH(Structured Exception Handling)이라고 말하면 Windows 자체적으로 지원하는 구조적 예외 처리를 의미합니다. (관련 키워드 : __try, __except, __finally, __leave)

그리고 C++ Exception Handling (이하 C++ EH) 라 하면 C++ 에서 정의하고 있는 구조적 예외 처리를 의미합니다. (관련 키워드 : try, catch, throw) 두 예외 처리 방식에 대해 간단히 설명드리겠습니다.

SEH 사용 예제
__try
{
    int a = 500;
    int b = a / 0; // 0으로 나누기
}
__except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
    // 예외 처리
}
이 것이 기본적인 SEH 사용법입니다.

__finally 키워드의 경우 예외가 발생하던 발생하지 않던 수행해야 되는 구문에서 쓰입니다.
DWORD FilterFunction()
{
   printf("1 ");                     // printed first
   return EXCEPTION_EXECUTE_HANDLER;
}

VOID main(VOID)
{
   __try
   {
       __try
       {
           RaiseException(
               1,                    // exception code
               0,                    // continuable exception
               0, NULL);             // no arguments
       }
       __finally
       {
           printf("2 ");             // this is printed second
       }
   }
   __except ( FilterFunction() )
   {
       printf("3\n");                // this is printed last
   }
}
위 코드 수행 후 1 2 3이 출력됩니다.

즉, 예외 발생했음에도 __finaly안의 구문은 수행됐음을 의미하죠.
DWORD FilterFunction()
{
    printf("1 ");                     // printed first
    return EXCEPTION_EXECUTE_HANDLER;
}

VOID main(VOID)
{
    __try
    {
           __try
           {
                // none         
           }
           __finally
           {
                   printf("2 ");             // this is printed second
           }
    }
    __except ( FilterFunction() )
    {
           printf("3\n");                // this is printed last
    }
}
위 코드 수행은 2만 출력됩니다.

예외 발생 유무에 상관 없이 __finaly 안의 구문은 수행되는 것이죠. __leave 구문은 __try 구문안에서 빠져나가고자 할 때 사용됩니다.

DWORD FilterFunction() { printf("1 "); // printed first return EXCEPTION_EXECUTE_HANDLER; } VOID main(VOID) { __try { __try { printf("try"); __leave; RaiseException( 1, // exception code 0, // continuable exception 0, NULL); // no arguments } __finally { printf("2 "); // this is printed second } } __except ( FilterFunction() ) { printf("3\n"); // this is printed last } }
위 코드 수행시 try 2 가 수행됩니다. __leave가 수행됐음에도 __finaly가 안에 포함된 코드가 수행됐음을 알 수 있죠. 이상이 SEH의 기본적인 사용법이었습니다. C++ EH 사용 예제
try
{
    throw "Memory allocation failure!";
}
catch( char * str )
{
    std::cout << "Exception raised: " << str << '\n';
}
위 코드 수행시 Exception raised : Memory allocation failure! 문장이 출력됩니다.

즉, try {} 는 수행 구문, catch(캐치할 예외 타입) {}, throw 예외 로 처리되는 것입니다. 여기서 주의할 점은 throw; 는 예외의 전파로써 사용이 되기도 한다는 점 입니다.

여기서 모호한 점은 throw; 가 try안에서 사용될 때와, catch에서 사용 될 때와 차이가 있다는 점입니다.
VOID main(VOID)
{
   try
   {
       try
       {
           throw "Memory allocation failure!";
       }
       catch( char * str )
       {
           std::cout << "Exception raised: " << str << '\n';
           throw;
       }
   }
   catch(...)
   {
       std::cout << "catched" << '\n';
   }
}
위 코드의 경우가 catch에서 예외의 재전파에서 사용되는 예입니다.

위 구문 수행시 Exception raised : Memory allocation failure! catched 가 출력됩니다. 만약 예외만 발생시키려 throw; 를 try안에서 사용했을경우를 볼까요?
try
{

try

{

throw;

}

catch( ... )

{

std::cout << "Exception raised: \n";

}
}
catch(...)
{

std::cout << "catched" << '\n';
}

위 코드를 수행할 경우 예외가 발생하게 됩니다.


C++ 표준 15.1.8에 따르면
"If no exception is presently being handled, executing a throw-expression with no operand calls std::terminate()."
즉, 현재 예외가 없는데 throw; 하면 프로그램이 종료됩니다. 예외는 함수 경계를 넘어서 전파될 수 있으므로 throw; 가 문법적으로 catch 안에 있을 필요는 없습니다.

위 내용에 대해서는 아래 링크를 따라가 보시면, 논의가 이루어졌습니다.

KLDP의 try 안에서의 throw에 대한 논의 : http://kldp.org/node/106380

SEH to C++ EH

SEH 를 C++ Exception으로 자동으로 변환하도록 만들었을 때의 장점은 0xC0000005 같은 잘못된 메모리 참조 예외같은 하드웨어 예외까지도 C++ EH를 사용해서 한곳에서 감지할 수 있다는 점이 될 수 있겠죠.

다행이도 Windows Exception이 발생했을 때 콜백 받을 수 있는 함수가 존재하므로, 아주 간단한 구현이 가능합니다.

_set_se_translator( TranslateSEHtoCE );
위와 같이 해주면, Windows Exception이 발생할 때마다 TranslateSEHtoCE 이라는 이름의 함수가 호출됩니다.

TranslateSEHtoCE안에서는 C++ Exception을 발생시키면 되겠죠.
 
예제
// a C++ exception class that contains the SEH information
class CSEHException
{
public:
       CSEHException( UINT code, PEXCEPTION_POINTERS pep)
       {
                  m_exceptionCode        = code;
                  m_exceptionRecord    = *pep->ExceptionRecord;

                  m_context            = *pep->ContextRecord;

              _ASSERTE(m_exceptionCode == m_exceptionRecord.ExceptionCode);
       }

       operator unsigned int() { return m_exceptionCode; }

       // same as exceptionRecord.ExceptionCode
       UINT m_exceptionCode;

       // exception code, crash address, etc.
       EXCEPTION_RECORD m_exceptionRecord;

       // CPU registers and flags
       CONTEXT m_context;
};

// the SEH to C++ exception translator
void _cdecl TranslateSEHtoCE( UINT code, PEXCEPTION_POINTERS pep)
{
      throw CSEHException(code, pep);
}


int main(int argc, char* argv[])
{
       // install the translator
       _set_se_translator( TranslateSEHtoCE);


       try
       {
                  char* p = NULL;

              *p = 'A';
       }
       catch( CSEHException& e)
       {

              if( EXCEPTION_ACCESS_VIOLATION == e)
                  {

                      _RPT0( _CRT_WARN, "Access Violationn");
                  }
       }
       return 0;
}
참고 자료


Posted by 엘키 엘키

댓글을 달아 주세요

char* GetStr()
{
	static char szStr[] = "Hello";
	return szStr;
}

void PrintStr(char **str)
{
	printf("%s",*str);
}


int main(int argc, char **arv)
{
	PrintStr(&GetStr());
	return 0;
}

 

위 코드는 아래와 같은 컴파일 에러를 발생시킨다.

error C2102: '&' requires l-value


컴파일러의 에러는, l-value. 즉, 어딘가에 저장된 값에만 주소 연산자를 사용할 수 있다는 말이다.

내가 이 코드를 작성한 의도는 char형 포인터의 포인터 (이중 포인터)를 매개 변수로 받는 PrintStr함수의 매개변수로, char형 포인터를 반환하는 GetStr함수의 반환 값의 주소를 넘기면 정상적으로 동작할 거라는 생각에서였다.


그래서 내가 생각한 해답은, GetStr함수가 반환할 char형 포인터 주소인

GetStr() 0x00427b60 "Hello"           char *
가 아니라, GetStr의 반환 값이 임시 저장되어 있는, 레지스터의 주소를 대입하려 하는 건 아닐까 생각했다.

 
main함수를 이렇게 바꾸면 정상적으로 동작한다.

int main(int argc, char **arv)
{
	char *str = GetStr();
	PrintStr(&str);
	return 0;
}
이 코드를 디스 어셈블 해본 결과다.
char *str = GetStr();

00412BDE  call        GetStr (411500h) 

00412BE3  mov         dword ptr [str],eax
역시 추측이 맞았다. GetStr함수를 부른 결과는 eax 레지스터에 저장되어 있었고, 그 값을 어딘가에 복사하기 전까진, 레지스터에 있는 값이므로, 레지스터에 주소 연산자를 사용할 수 없기 때문에 컴파일 에러를 낸 것이었다.

Posted by 엘키 엘키

댓글을 달아 주세요

  1. HooLi 2009.12.11 13:54  댓글주소  수정/삭제  댓글쓰기

    엘키 님 천재.


블로그 이미지
Software Engineer
엘키

공지사항

Yesterday31
Today27
Total1,605,481

달력

 « |  » 2020.8
            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 29
30 31          

글 보관함