Static Library 제작시 Name Conflict 피하기 ( C2084 )
MYDN2008. 10. 21. 15:29
namespace 에 대해 오해하고 있는 소수의 분들을 위해...
Static Library 를 제작하고, 또 사용하다보면 Name Conflict 에러가 발생하는 경우가 있습니다.
이 에러는 프로젝트에 포함된 이종 라이브러리가 같은 이름의 함수명/변수명을 갖게 될 경우 나타납니다.
가령, 프로젝트에서 a.lib , b.lib 두개의 라이브러리를 link 할때에
a.lib 에서 fnA() 라는 함수가 선언되어있고,
b.lib 에서 fnA() 라는 함수가 선언되어있는 경우
두 함수의 이름이 같기 때문에 발생하는 에러입니다.
이 때 conflict 를 피하기 위하여 생긴것이 namespace 입니다.
namespace 는 내부에 정의된 이름들에 대해 마치 class의 캡슐화와 같은 효과를 줍니다.
.net 프로그래밍을 하셨다면,
using namespace std;
라는 구문을 보셨을텐데요,이 명령은
소스코드 내에서 cout 등의 함수들을 호출할때
std::cout 을 호출하는것으로 인식하게 해 줍니다.
즉, 표준 함수의 namespace 가 std 인 셈입니다.
개발을 하다보면,
동일 소스를 사용해서 이종 Static Library 를 생산해야할 때가 있습니다.
이럴때에는 소스가 동일하기 때문에 똑같은 이름의 함수/변수를 갖는 Static Library 가 생기게 됩니다.
( 물론 소스를 복사해서 쓸수도 있지만, 동일 소스가 여러개로 보관되면 관리상의 치명적인 문제점이 생기게 되겠죠 )
이럴때, 프로젝트를 기준으로 하여
소스가 동일하더라도, 프로젝트별 namespace 를 달리하게 되면
하나의 코드로 여러개의 library 를 만들더라도 conflict 문제 없이 빌드할 수 있습니다.
일반적으로 namespace 를 사용하는 소스의 예는 다음과 같습니다.
using namespace myprojectns;
namespace myprojectns{
}
namespace myprojectns{
int g_nCount;
void fnA(){
Sleep(0);
}
}
마치 Class 와 비슷한 모양을 갖게 되는데요
위의 예에서는 myprojectns 로 설정된 네임스페이스 내에
직접 코딩을 하게 되면 외부의 소스에서는 접근할때에 ( extern 되어있다고 가정 )
myprojectns::g_nCount
myprojectns::fnA()
로 접근 할 수 있습니다.
다음은 이를 간단히 적용하기 위한 매크로 입니다.
// MyNamespace.h
#pragma once
#pragma once
#if defined(_LIB_SDK_PROJECT_Y)
#define MYNAMESPACE MYNAMESPACE_LIB_PROJECT_Y
#elif defined(_LIB_SDK_PROJECT_Z)
#define MYNAMESPACE MYNAMESPACE_LIB_PROJECT_Z
#else
#error MYNAMESPACE Required. Check MyNamespace.h
#endif
#define MYNAMESPACE MYNAMESPACE_LIB_PROJECT_Y
#elif defined(_LIB_SDK_PROJECT_Z)
#define MYNAMESPACE MYNAMESPACE_LIB_PROJECT_Z
#else
#error MYNAMESPACE Required. Check MyNamespace.h
#endif
#define MN MYNAMESPACE
#define _START_OF_INTERNAL_SRC namespace MYNAMESPACE {using namespace MYNAMESPACE ;
#define _END_OF_INTERNAL_SRC }
#define _SO_INTERNAL_SRC _START_OF_INTERNAL_SRC
#define _EO_INTERNAL_SRC _END_OF_INTERNAL_SRC
#define _EO_INTERNAL_SRC _END_OF_INTERNAL_SRC
_LIB_SDK_PROJECT_Y 는 Project의 Properties 에서 설정한 Preprocessor definitions 입니다.
Project별로 다른 정의를 주어야하는 부분입니다.
위의 매크로는 Preprocessor definitions 에서 프로젝트 특성을 읽어
서로 다른 namespace 를 적용할 수 있도록 해줍니다.
예에서는 Preprocessor definitions 가
_LIB_SDK_PROJECT_Y 인경우 MYNAMESPACE_LIB_PROJECT_Y
_LIB_SDK_PROJECT_Z 인경우 MYNAMESPACE_LIB_PROJECT_Z
의 namespace 가 정의됩니다.
cpp 소스에서의 사용은 다음과 같습니다.
// _LIB_SDK_PROJECT_Y 의 소스일 때
#include <stdio.h>
#include "MyNamespace.h"
_SO_INTERNAL_SRC
VOID fnA()
{
#include <stdio.h>
#include "MyNamespace.h"
_SO_INTERNAL_SRC
VOID fnA()
{
...
}
BOOL fnB()
{
BOOL fnB()
{
...
}
_EO_INTERNAL_SRC
_EO_INTERNAL_SRC
헤더파일을 포함하고, 소스의 위 아래를 _SO_ , _EO_ 매크로로 감싸주면
헤더에 정의된 namespace 가 project 별로 적용됩니다.
외부에서 위 소스의 함수를 호출하고자 할때에는 다음과 같이 호출할 수 있습니다.
//_LIB_SDK_PROJECT_Y 라이브러리를 링크했을 때
#include <stdio.h>
void initialEntry()
{
}
#include <stdio.h>
void initialEntry()
{
MYNAMESPACE_LIB_PROJECT_Y::fnA();
MYNAMESPACE_LIB_PROJECT_Y::fnB();
MYNAMESPACE_LIB_PROJECT_Y::fnB();
}
혹은,
//_LIB_SDK_PROJECT_Y 라이브러리를 링크했을 때
#include <stdio.h>
using namespace MYNAMESPACE_LIB_PROJECT_Y;
void initialEntry()
{
}
#include <stdio.h>
using namespace MYNAMESPACE_LIB_PROJECT_Y;
void initialEntry()
{
fnA();
fnB();
fnB();
}
외부 library 제작이 필요없는 실행파일 기반 프로젝트이거나
소형 프로젝트일 경우에는 namespace를 사용하지 않아도 될것 같습니다.
하지만, 여러명이 작업하는 프로젝트이거나 ( 소스별 extern function name conflict 문제 )
static library 를 만드는 프로젝트일 경우에는
사용할 필요성이 높아 보이는 부분입니다.
-------------------------------------------------------------------------------------------
덧붙혀서,
매크로를 자세히 보신 분은 MYNAMESPACE 가 MN 으로 정의된걸 보셨을텐데요
이는 내부에서의 사용편의성을 위한 정의입니다.
가령, 배포용 라이브러리를 제작할때에
내부소스에서 사용되는 함수/변수들은 namespace를 주고,
extern 되는 함수에 대해서는
(사용편의성을 위해) namespace없이 unique한 이름으로 정의하는 경우
내부에서 사용되는 소스의 경우 _SO_, _EO_ 매크로를 사용하고,
외부에 extern 되는 소스는 _SO_, _EO_ 매크로의 사용없이
MN::fnA() 등과 같이 바로 접근할 수 있도록 정의한 부분입니다.
가령,
// extern source
#include <stdio.h>
#include "MyNamespace.h"
extern "C" void fnProjectInit()
{
#include <stdio.h>
#include "MyNamespace.h"
extern "C" void fnProjectInit()
{
MN::fnA();
}
( extern의 위치는 이해의 편의를 위해... )
lib 제작시 외부에 제공되는 함수는 위의 모습처럼 _SO_, _EO_ 없이 사용하고,
내부에서 다른소스의 함수를 가져다 쓰는 형태를 만들어주면,
외부에서는 namespace 없이 fnProjectInit()을 바로 호출할 수 있고,
소스의 내부에 정의된 함수나 변수이름등은 중복확률이 낮아지게 되는 효과를 갖게 됩니다.