C++에서 2진수 형태의 상수 표현
새로 작업하는 일이 시스템 관련 일이라서 매번 2진수를 16진수로 머리속에 생각해면서 입력하다보니, 지치기도 하고 순간 잘못 판단해서 엉뚱한 값이 들어가버리도 하는 상황에서 C++ 에서 2진수 상수를 입력할 수 있는 템플릿을 한번 만들어 보았습니다.
기본적인 아이디어는 meta programming 쪽에서 기본적인 재귀용법을 차용한 것인데, 기존의 방법들이 2진수로 표현하고 싶은 상수가 8진수의 형태일 때 (예를 들어서, 01011010 같은) 문제점이 있는 것도 있고, 매크로만으로 구현된 BOOST_BINARY 같은 경우도, 정확한 2진수 입력을 (예를 들어서 2진수 숫자가 안되는 10102900 같은 것을 컴파일시 에러로 처리) 강제할 때, 사용자에게 잘못을 정확하게 지적할 수 없다는 것이 불만이었습니다.
처음 구현은 입력된 템플릿 인자가 8진수인지 10진수인지를 구분해서 처리하도록 했는데, 두 진수간에 겹쳐지는 숫자가 있어서 0과 1로만 된 숫자를 강제할 수 없는 문제가 생겼습니다. 이것을 구분하고 처리하는데 엄청나게 신경을 썼는데, 결국 무용지물... 코드도 길고, 마음에 안들긴 했습니다...
고민고민 하다가 느닷없이 번쩍 생각이 나서 몇줄 테스트해보고 작동하는 것을 확인 해보니, 너무 간단하게 해결이 되어버려서 너무 허탈하더군요. 이것은 매크로의 붙이기 연산자를 이용해서 입력된 숫자를 16진수 표현으로 바꿔서 처리하는 것입니다. (수정: 8진수 표현으로)
개인적으로 사용하는 데에는 아직까지 별 문제점이 없습니다만, 여러분들이 보시기에 미진한 점이 많을 것으로 생각합니다. 한번 보시고 문제점이나 버그, 개선점이 있다면 좋은 말씀 부탁드립니다.
감사합니다.
p.s. 이런 작은 규모의 코드는 kldp.net 프로젝트에 올리기에 좀 뭐해서 그냥 블로그 형태로 남깁니다.
----- binconst.h
template< int > struct error_if_not; template<> struct error_if_not< true > { }; template< unsigned int N > struct binary_raw { static unsigned int const value = N % 8 + 2 * binary_raw< N / 8 >::value; static int const valid = ((N % 8) <= 1) * binary_raw< N / 8 >::valid; }; template<> struct binary_raw< 0 > { static unsigned int const value = 0; static int const valid = 1; }; template< unsigned int B0 > struct binary_byte { error_if_not< B0 <= 011111111 > OUT_OF_RANGE; error_if_not< binary_raw< B0 >::valid > INVALID_BINARY_DIGIT; static unsigned int const value = binary_raw< B0 >::value; }; template< unsigned int B1, unsigned int B0 > struct binary_word { error_if_not< B0 <= 011111111 > OUT_OF_RANGE_AT_B0; error_if_not< binary_raw< B0 >::valid > INVALID_BINARY_DIGIT_AT_B0; error_if_not< B1 <= 011111111 > OUT_OF_RANGE_AT_B1; error_if_not< binary_raw< B1 >::valid > INVALID_BINARY_DIGIT_AT_B1; static unsigned int const value = (binary_raw< B1 >::value << 8) | (binary_raw< B0 >::value); }; template< unsigned int B3, unsigned int B2, unsigned int B1, unsigned int B0 > struct binary_dword { error_if_not< B0 <= 011111111 > OUT_OF_RANGE_AT_B0; error_if_not< binary_raw< B0 >::valid > INVALID_BINARY_DIGIT_AT_B0; error_if_not< B1 <= 011111111 > OUT_OF_RANGE_AT_B1; error_if_not< binary_raw< B1 >::valid > INVALID_BINARY_DIGIT_AT_B1; error_if_not< B2 <= 011111111 > OUT_OF_RANGE_AT_B2; error_if_not< binary_raw< B2 >::valid > INVALID_BINARY_DIGIT_AT_B2; error_if_not< B3 <= 011111111 > OUT_OF_RANGE_AT_B3; error_if_not< binary_raw< B3 >::valid > INVALID_BINARY_DIGIT_AT_B3; static unsigned int const value = (binary_raw< B3 >::value << 24) | (binary_raw< B2 >::value << 16) | (binary_raw< B1 >::value << 8) | (binary_raw< B0 >::value); }; #define BB_( b0 ) \ binary_byte< 0##b0 >::value #define BW_( b1, b0 ) \ binary_word< 0##b1, 0##b0 >::value #define BD_( b3, b2, b1, b0 ) \ binary_dword< 0##b3, 0##b2, 0##b1, 0##b0 >::value
----- binconst_test.cpp
#include "binconst.h" const dword aaa = BD_( 11111111, 00000000, 00000000, 00000101 ); #define CHECK( c ) \ { \ error_if_not< (c) > error; \ (void)error; \ } void test() { /* Generated assembly code Visual C++ 2008 Express Edition mov DWORD PTR _bbb$[ebp], -16777211 ; ff000005H g++ (GCC) 4.2.1 20070719 [FreeBSD] movl $-16777211, -4(%ebp) */ dword bd = BD_( 11111111, 00000000, 00000000, 00000101 ); CHECK( BD_( 11111111, 00000000, 00000000, 00000101 ) == 0XFF000005 ); CHECK( BB_( 0 ) == 0x0 ); CHECK( BB_( 1 ) == 0x1 ); CHECK( BB_( 00 ) == 0x0 ); CHECK( BB_( 01 ) == 0x1 ); CHECK( BB_( 10 ) == 0x2 ); CHECK( BB_( 11 ) == 0x3 ); CHECK( BB_( 000 ) == 0x0 ); CHECK( BB_( 001 ) == 0x1 ); CHECK( BB_( 010 ) == 0x2 ); CHECK( BB_( 011 ) == 0x3 ); CHECK( BB_( 100 ) == 0x4 ); CHECK( BB_( 101 ) == 0x5 ); CHECK( BB_( 110 ) == 0x6 ); CHECK( BB_( 111 ) == 0x7 ); CHECK( BB_( 0000 ) == 0x0 ); CHECK( BB_( 0001 ) == 0x1 ); CHECK( BB_( 0010 ) == 0x2 ); CHECK( BB_( 0011 ) == 0x3 ); CHECK( BB_( 0100 ) == 0x4 ); CHECK( BB_( 0101 ) == 0x5 ); CHECK( BB_( 0110 ) == 0x6 ); CHECK( BB_( 0111 ) == 0x7 ); CHECK( BB_( 1000 ) == 0x8 ); CHECK( BB_( 1001 ) == 0x9 ); CHECK( BB_( 1010 ) == 0xA ); CHECK( BB_( 1011 ) == 0xB ); CHECK( BB_( 1100 ) == 0xC ); CHECK( BB_( 1101 ) == 0xD ); CHECK( BB_( 1110 ) == 0xE ); CHECK( BB_( 1111 ) == 0xF ); CHECK( BB_( 00000000 ) == 0x00 ); CHECK( BB_( 00000001 ) == 0x01 ); CHECK( BB_( 00000010 ) == 0x02 ); CHECK( BB_( 00000011 ) == 0x03 ); CHECK( BB_( 00000100 ) == 0x04 ); CHECK( BB_( 00000101 ) == 0x05 ); CHECK( BB_( 00000110 ) == 0x06 ); CHECK( BB_( 00000111 ) == 0x07 ); CHECK( BB_( 00001000 ) == 0x08 ); ... ... ... CHECK( BB_( 11111000 ) == 0xF8 ); CHECK( BB_( 11111001 ) == 0xF9 ); CHECK( BB_( 11111010 ) == 0xFA ); CHECK( BB_( 11111011 ) == 0xFB ); CHECK( BB_( 11111100 ) == 0xFC ); CHECK( BB_( 11111101 ) == 0xFD ); CHECK( BB_( 11111110 ) == 0xFE ); CHECK( BB_( 11111111 ) == 0xFF ); }
댓글
Out_Of_Range 가 정상
컴파일시 Out_Of_Range 가 정상 동작하려면 16진수를 8진수로 표현해야 하는군요.
32비트 8진수의 자리수가 11이므로 나누기 부분을 8로 바꾸고 비교를 0x11111111 이 아니라 011111111 로 하고 매크로의 0x 를 0 으로 바꾸면 잘 동작합니다.
저라면..
저라면 이렇게 짜겠습니다: (물론 Template 사용이 목적이시라면 논외가 되겠습니다만..)
#define BB_00000000 0x00
#define BB_00000001 0x01
#define BB_00000010 0x02
...
#define BB_11111111 0xff
#define BW(b1,b0) ((b1)<<8 | (b0))
#define BD(b3,b2,b1,b0) ((b3)<<24 | (b2)<<16 | (b1)<<8 | (b0))
컴파일시에 검사되고, 검증할 필요도 없고, C 에서도 사용가능합니다.
인용:컴파일시에
네 매크로이면 컴파일시에 검사가 되겠지만, 에러 메시지는 정의되지 않았다고 나올 뿐 무엇이 잘못이라는 것을 지적하지 않습니다.
매크로라도 검증할 필요가 있습니다.
네 맞습니다.
그리고 제시하신 형태로는 짧은 형태의 이진수를 입력할 수 없습니다. 예를들어 0111 같은 니블 형태의 매크로를 전부 만들어야한다는 것이겠죠.
네..
네.. 말씀하신데로 인용하신 부분은 표현이 좀 과했죠. --;
죄송..
컴파일러가 좀 더
컴파일러가 좀 더 빨리 에러를 찾고 정확한 위치를 지적할 수 있도록 수정하였습니다.
#include #include
이 코드는 에러를 발생시키지 않네요.
어줍잖게 수정했다가
어줍잖게 수정했다가 실수했군요.
컴파일러가 빨리 에러를 정확하게 지정할 수 있도록 수정중입니다.
수정된 것으로
수정된 것으로 교체하시면 원하시는 결과를 보실 수 있습니다.
관심 주셔서 감사드립니다.
p.s. 처음 버전을 만들다 삽질하면서 알게된 템플릿 안에서 && 논리는 곱하기 연산으로 구현할 수 있다는 것을 이용하였습니다.
C 와 asm 으로 작성된
C 와 asm 으로 작성된 리눅스 커널에서, bit field 를 어떻게 다루는 지 짧게 소개드리겠습니다.
첨부된 파일은 GPL 이며 copyright 는 다음과 같습니다.
간단히 비트필드 몇개와 매크로를 정의해보겠습니다.
bitfield.h 에서 제공하는 것은 골격에 해당하는 매크로일 뿐이고,
range check 등은 이 매크로들을 이용해서 재생산된 매크로(혹은 함수)에서 행합니다.
십년 가까이 이 매크로들을 애용해오고 있는데요,
이건 개발자의 노고를 덜어주려는 자상한 배려라기 보단 해커 특유의 삐딱선이라는 느낌이 강하게 듭니다.
개발자의 실수를 줄여주고,
데이타시트나 기타 문서를 참조하지 않고 코드만으로 대화가 가능하고,
...이건 헛소리.
이진수의 조합과 해석에 쓰는 시간은 비웃음거리.
그것을 안하기 위해선 무슨 짓이건 얼마든 하겠다.
... 이게 정답.
OTL
댓글 달기