#define의 쓰임
``c #define``은 옵션이나 flag를 지정할 때만 사용하고, 다른 용도일 경우 ``c inline``이나 ``c static const``를 사용하는 편이 좋다.
이렇게 할 경우 1. type-safe하고, 2. 어디에 쓰느냐에 따라 scope도 지정 가능하며, 3. 디버깅 심볼이 생성되어 디버깅이 편하다는 장점이 있다.
간단한 함수 차원의 #define / inline
C에서 사칙연산 같은 간단한 작업은 ``c #define macro``를 활용해 왔으나, 요즘은 C에서도 inline이 가능하므로 이 쪽을 권장한다.
```c
extern inline void f() { }
```
C++에서도 ``c #define``은 꼭 필요한거 아니면 쓰지 않는 것을 권장한다. (Google C++ Style Guide)
단순 치환으로 call 없이 in-line 되기 때문에 calling overhead를 줄일 수 있고 좀 더 간결해진다.
또한 컴파일 타임(정확히는 전처리)에 단순 치환된다는 점을 고려해보면 여러가지로 활용이 가능하다.
C++에서는 ``cpp inline`` 키워드를 붙이면 in-line 함수를 만들 수 있다.
C++에서 .h와 .cpp를 분리하는 경우라면, 인라인 함수의 바디는 반드시 .h에 들어가야 한다.
이는 컴파일러에게 이를 인라이닝하라는 힌트를 주는 것이나 다름 없기 때문에, ``cpp inline`` 키워드를 붙이지 않아도 대체로 인라이닝된다.
inline은 함수 구현부가 함수 호출 부분에 그대로 치환되어 별다른 call 없이 함수의 기능을 수행하는 것을 의미한다. 따라서 calling overhead를 줄일 수 있다.
상수 차원의 #define / static const / enum
``c #define``은 지양하고, ``c const``나 ``c enum`` 중 골라쓰면 되는데, 두 개의 용도가 다르기 때문에 상황에 맞게 쓰면 된다.
나는 보통 ``c enum``을 쓰고 문자열같이 ``c enum``을 사용할 수 없는 경우 ``c const char*``를 사용한다.
``c const``는 함수 내부에서 사용하고 상수(e.g., ``c BUF_SIZE``)를 정의할 때는 ``c enum [class]``을 사용하는 편이 좋겠다.
다만 다음 처럼 ``c static`` 변수의 크기를 결정할 때는 ``c #define``을 사용하거나, 직접 입력해야 한다.
```c
#define PATH_LEN_D 15
static const int PATH_LEN_C = 15;
enum : int {
BUF_SIZE = 4096
}
char* parse_something(const char* dev) {
static __thread char ret_str[PATH_LEN_C]; // error!
snprintf(ret_str, PATH_LEN_D, "/aaa/%s/ccc", dev);
return ret_str;
}
>>> error: storage size of ‘ret_str’ isn’t constant
```
예제
```c
enum NI : int { // or enum class
MAXHOST = 1025,
NUMERICHOST = 1
};
namespace Path {
const char* SYS_NET = "/sys/class/net/%s/address";
const char* PROC_ARP = "/proc/net/arp";
};
```
enum VS enum class
``cpp enum``과 ``cpp enum class``는 다르다. 전자는 C언어에서 사용하는 방식이고, 후자는 C++11 이후 그리고 대부분의 언어에서 사용하는 방식이다.
전자의 경우 스코프 문제가 있다. enum 이름을 명시하지 않아도 접근 가능하다는 점인데, 즉 전역 네임스페이스를 사용하기 때문에 C++에서는 ``cpp namespace``로 묶어주는 방식을 사용해야만 한다.
```cpp
namespace Len {
enum T : int {
IP_STR_BUF = 16
};
}
/** external source **/
sysinfo::IP_STR_BUF // OK.
sysinfo::Len::IP_STR_BUF // OK.
```
그러나 후자는 클래스처럼 이름을 명시해야 사용 가능하며 따라서 네임스페이스를 추가로 사용할 필요가 없다. 이 밖에도 type safety, 선언 정의 분리 등의 장점이 있다.
전반적으로 타 언어와 통일성이나 여러 장점 때문에 ``cpp enum class``를 사용하는 편이 더 좋기는 한데,
이건 배열의 크기로 들어갈 때 자동으로 캐스팅이 안되서 다음과 같이 해줘야 하는 번거로움이 있다.
그래서 배열 크기로 지정할 상수가 필요할 때는 그냥 ``c enum``을 사용하는게 편리한 것 같다.
```cpp
/** enum class **/
enum class Len {
IP_STR_BUF = 16
};
char src_ip_str[static_cast<int>(Len::IP_STR_BUF)];
/** enum **/
enum Len : int {
IP_STR_BUF = 16
};
char src_ip_str[Len::IP_STR_BUF];
```
결론적으로 어떤 클래스에 속하지 않고 외부에 위치하는 ``c enum``은 다음과 같이 enum 이름을 적지 않으면서 네임 스페이스로 감싸주는 방법을 사용하면, static casting을 하지 않아도 되고 namespace 충돌도 피할 수 있다.
단, enum을 파라미터로 받을 때 네임스페이스 이름만 명시할 수 없으니, ``cpp Len::T param`` 같은 식으로 받을 수 있게 T를 적어준다.
```c
namespace sysinfo {
....
namespace Len {
enum T : int {
IP_STR_BUF = 16,
MAC_STR_BUF = 18,
SYS_NET_PATH = 24,
INT_NAME_MAX = 15,
ARP_TABLE_ENTRY = 128
};
}
....
}
```
어떤 클래스에 속하는 ``cpp enum``의 경우, 클래스 내부에서 ``cpp namespace``를 지정할 수 없고, 그렇게 할 필요도 없는게 어차피 클래스 스코프로 감싸지기 때문이다. 그냥 enum 이름 지정해서 사용하면 된다.