C++에서 2진수 형태의 상수 표현

bugiii의 이미지

새로 작업하는 일이 시스템 관련 일이라서 매번 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 );
}

댓글

bugiii의 이미지

컴파일시 Out_Of_Range 가 정상 동작하려면 16진수를 8진수로 표현해야 하는군요.

32비트 8진수의 자리수가 11이므로 나누기 부분을 8로 바꾸고 비교를 0x11111111 이 아니라 011111111 로 하고 매크로의 0x 를 0 으로 바꾸면 잘 동작합니다.

setup74의 이미지

저라면 이렇게 짜겠습니다: (물론 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 에서도 사용가능합니다.

bugiii의 이미지

Quote:
컴파일시에 검사되고

네 매크로이면 컴파일시에 검사가 되겠지만, 에러 메시지는 정의되지 않았다고 나올 뿐 무엇이 잘못이라는 것을 지적하지 않습니다.

Quote:
검증할 필요도 없고

매크로라도 검증할 필요가 있습니다.

Quote:
C 에서도 사용가능합니다.

네 맞습니다.

그리고 제시하신 형태로는 짧은 형태의 이진수를 입력할 수 없습니다. 예를들어 0111 같은 니블 형태의 매크로를 전부 만들어야한다는 것이겠죠.

setup74의 이미지

네.. 말씀하신데로 인용하신 부분은 표현이 좀 과했죠. --;
죄송..

bugiii의 이미지

컴파일러가 좀 더 빨리 에러를 찾고 정확한 위치를 지적할 수 있도록 수정하였습니다.

lifthrasiir의 이미지

#include &lt;cstdio&gt;
#include "binconst.h"
 
int main() {
	std::printf("%#x\n", BD_(3000000, 00000000, 00000000, 00000000));
	return 0;
}

이 코드는 에러를 발생시키지 않네요.

bugiii의 이미지

어줍잖게 수정했다가 실수했군요.

컴파일러가 빨리 에러를 정확하게 지정할 수 있도록 수정중입니다.

bugiii의 이미지

수정된 것으로 교체하시면 원하시는 결과를 보실 수 있습니다.

관심 주셔서 감사드립니다.

p.s. 처음 버전을 만들다 삽질하면서 알게된 템플릿 안에서 && 논리는 곱하기 연산으로 구현할 수 있다는 것을 이용하였습니다.

bushi의 이미지

C 와 asm 으로 작성된 리눅스 커널에서, bit field 를 어떻게 다루는 지 짧게 소개드리겠습니다.
첨부된 파일은 GPL 이며 copyright 는 다음과 같습니다.

/*
 *      FILE            bitfield.h
 *
 *      Version         1.1
 *      Author          Copyright (c) Marc A. Viredaz, 1998
 *                      DEC Western Research Laboratory, Palo Alto, CA
 *      Date            April 1998 (April 1997)
 *      System          Advanced RISC Machine (ARM)
 *      Language        C or ARM Assembly
 *      Purpose         Definition of macros to operate on bit fields.
 */

간단히 비트필드 몇개와 매크로를 정의해보겠습니다.

#define fld_REG0_SOME1 Fld(4, 0)  /* [3:0] */
#define fld_REG0_SOME2 Fld(2, 16) /* [17:16] */
#define fld_REG0_SOME3 Fld(8, 24) /* [31:24] */
 
/* definition */
#define REG0_VAL(f, v)     FInsrt((v), fld_REG0_##f)
 
/* extraction */
#define REG0_EXT(f, x)     FExtr((x), fld_REG0_##f)
 
/* modification */
#define REG0_CHG(f, x, v)  (((x) & FMsk(fld_REG0_##f)) | REG_VAL(f, v))

bitfield.h 에서 제공하는 것은 골격에 해당하는 매크로일 뿐이고,
range check 등은 이 매크로들을 이용해서 재생산된 매크로(혹은 함수)에서 행합니다.

십년 가까이 이 매크로들을 애용해오고 있는데요,
이건 개발자의 노고를 덜어주려는 자상한 배려라기 보단 해커 특유의 삐딱선이라는 느낌이 강하게 듭니다.

개발자의 실수를 줄여주고,
데이타시트나 기타 문서를 참조하지 않고 코드만으로 대화가 가능하고,
...이건 헛소리.

이진수의 조합과 해석에 쓰는 시간은 비웃음거리.
그것을 안하기 위해선 무슨 짓이건 얼마든 하겠다.
... 이게 정답.

OTL

댓글 첨부 파일: 
첨부파일 크기
Plain text icon bitfield.h.txt2.79 KB

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.