네트워크 프로그래밍을 하다 보면 IP(IPv4)를 변수(혹은 객체)로 선언해서 사용하는 경우가 많습니다. IPv4의 경우 unsigned 32bit integer를 사용하는 것이 일반적이죠.


uint32_t ip;




IP type과 관련되어 코딩을 하게 되면 기본적으로 다음과 같은 것들이 필요합니다.


  • 1. String으로부터 또는 String으로의 변환하는 함수(Dotted decimal notation).
  • 2. LocalHost(127.x.x.x)인지 확인하는 함수.
  • 3. Broadcast IP인지 확인하는 함수.
  • 4. Multicast IP인지 확인하는 함수.
  • 5. 비교 연산, 산술 연산 및 비트 연산.




C 언어의 형식으로 디자인을 해 보자면 다음과 같이 선언할 수 있겠죠(snake_case).


1. string ip_to_str(uint32_t ip); uint32_t str_to_ip(string s);
2. bool is_local_ip(uint32_t ip);
3. bool is_broadcast_ip(uint32_t ip);
4. bool is_multicast_ip(uint32_t ip);
5. unit32_t는 기본적으로 비교, 산술, 비트 연산이 가능.




C++에서 Ip라는 클래스로 만들면 다음과 같이 선언할 수 있습니다(CamelCase).


class Ip
{
protected:
  uint32_t value;
public:
  Ip(string s); // 1번
  string str(); // 1번
  bool isLocalHost(); // 2번
  bool isBroadcast(); // 3번
  bool isMulticast(); // 4번
};


여기에서 문제는 5번입니다. IP 객체간에 비교, 산술, 비트 연산을 하기 위해서는 수많은 operator들을 overloading해야 하는데, 클래스 디자인이 복잡해 지고 오히려 배보다 배꼽이 더 커질 수가 있죠.




다음과 같은 변태(?)적인 생각도 해 보았습니다만, C++에서 primitive type은 상속이 되지 않는 다는 것을 컴파일하면서 알게 되고 좌절하게 됩니다. ㅠㅠ


class Ip : public uint32_t // compile error
{
};




여기에서 primitive type(본 예제에서는 uint32_t)에서 상속받은 것처럼 클래스를 디자인할 수 없을까 고민을 하게 되는데요, 다음과 같은 방법을 사용할 수가 있습니다.


class Ip
{
protected:
  uint32_t value;
public:
  Ip(string s); // 1번
  string str(); // 1번
  bool isLocalHost(); // 2번
  bool isBroadcast(); // 3번
  bool isMulticast(); // 4번
  Ip(const uint32_t value); // 5번
  operator uint32_t() const; // 5번
};




위의 코드처럼 해당 클래스를 primitive type과 관계된 conversion constructor와 cast operator를 선언해 주면 아래와 같이 다양한 연산이 가능해 지게 됩니다.


IP ip1, ip2, ip3;
ip3 =  ip1 + ip2;
ip3 =  ip1 - ip2;
ip3 =  ip1 * ip2;
ip3 =  ip1 / ip2;
ip3 =  ip1 & ip2;
ip3 =  ip1 | ip2;
ip3 =  ~ip1;




Ip라는 클래스를 Release 모드로 컴파일을 하면 uint32_t를 사용하는 것과 똑같이 작동하도록 최적화가 이루어 지므로, 속도에도 전혀 지장이 없습니다(Visual Studio, gcc에서 테스트해 봤음).


이상 primitive type을 클래스화해서 사용할 수 있는 허접 팁이었습니다. ^^