C++ 구조체 이니셜라이저 문제

struct CHAR_COLLECTION_DATA
{
    int CharID;
    int Value;
    int ValueCode;
};

CHAR_COLLECTION_DATA CollectionData  = {m_CharID, m_Value, m_ValueCode };

이런 코드가 있었다.

기능을 추가 하시려다보니 습관적으로

struct CHAR_COLLECTION_DATA
{
    int CharID;
    int ClassID; // 다른 변수를 중간에 추가함.
    int Value;
    int ValueCode;
};

CHAR_COLLECTION_DATA CollectionData  = {m_CharID, m_Value, m_ValueCode };

같은 코드고 컴파일 오류도 없지만 원래 코드와 다르게, CharID, Value, ValueCode를 채우지 않고, CharID, ClassID, Value에만 값을 채우는 코드가 되어버렸다.

물론 사용 코드를 전부다 훓어보지 않은 문제가 있긴 하지만, 컴파일 오류로 강제되지 않은 초기화도 좋은 제약은 아니다.

struct CHAR_COLLECTION_DATA
{
    int CharID;
    int ClassID; // 다른 변수를 중간에 추가함.
    int Value;
    int ValueCode;

    CHAR_COLLECTION_DATA()
                : CharID(0)
        , ClassID(0)
        , Value(0)
        , ValueCode(0)
    {

    }


    CHAR_COLLECTION_DATA(int charID, int classID, int value, int valueCode)
        : CharID(charID)
        , ClassID(classID)
        , Value(value)
        , ValueCode(valueCode)
    {

    }
};

기존 코드가 이렇게 짜여져 있었다면, 기존 코드였던

CHAR_COLLECTION_DATA CollectionData(m_CharID, m_Value, m_ValueCode);

는 컴파일 오류를 일으키기 때문에, 자연스레

CHAR_COLLECTION_DATA CollectionData(m_CharID, m_ClassID, m_Value, m_ValueCode);

이와 같이 고치게 될 것이다.

물론 모든 상황에서 주의 깊게 코드를 찾아보고 고치면 얼마나 좋겠냐 만은, 커버리지는 높을수록 좋은 것.

컴파일 오류로 막을 수 있는 습관은 갖추는 게 좋다.

구조체 이니셜라이저는 자제 하자.

첨언을 하자면, 대입될 변수를 지정하는 방식의 이니셜라이저는 추천이다. legacy한 문법을 허용하는 C스타일 구조체 이니셜라이저를 자제하자는 얘기다.

(서평) 좋은 코딩 나쁜 코딩 - 읽기 쉬운 코드가 좋은 코드다

제가 여러번 극찬한 임백준씨의 번역서입니다.

얼마나 좋았으면 직접 번역을 하셨을까 궁금하더군요.

이전에 번역하신 해커와 화가도 두고 두고 읽을만큼 색다른 시각이 좋았던지라, 이 책에 대한 기대도 그만큼 컸다고 볼 수 있습니다.

대략 책 제목에서도 알 수 있듯이, 어떠한 코드가 읽기 좋은 코드인지에 대한 기준을 제시하는 책입니다.

가장 유용했던 내용이라면 주석에 대한 내용입니다. Doxygen을 쓰다보니 강제당한 주석이 너무 당연한 내용을 너무 많이 담고 있었습니다.

물론 제가 Doxygen 규격을 따라가다 느낀 장점은, 당연한 내용을 작성하던 중에 다시 한번 생각하게 된다라는 점이었는데요, 그런 점을 제외하고는 사실 커다란 이득을 보기 어려웠던게 사실입니다.

특히나 최초 작성시에는 그렇다쳐도, 추후 유지보수에는 더더욱이 큰 메리트는 없다고 볼 수 있었습니다.

그렇다고 주석이 필요 없는 코드가 좋다고 항변하기엔, 그런 코드만 작성할 수 있는 것도 아니고 말이죠.

생각을 기록하라라는 이야기도 꽤나 크게 와닿았습니다.

또, 결함을 설명하라는 이 책에 대한 만족도를 크게 높여줬다고 볼 수 있었죠.

  • TODO: 아직 하지 않은일
  • FIXME: 오작동을 일으킨다고 알려진코드
  • HACK: 아름답지 않은 해결책
  • XXX: 위험! 여기에 큰 문제가 있다.
  • TextMate:ESC (이게 뭔뜻인지 아시는분?)

조금 아쉬웠던 것은, 저도 반대하면서도 장점과 단점을 명확히 제시하고 싶었던 goto에대한 설명이 아주 짧게 쓰여있다는 점이었습니다만…뭐 그분들도 제대로 설명하기 어려웠던 걸려나요?

아참! 제가 요새 절실히 깨닳고 있는, 코드 분량줄이기 챕터도 아주 좋습니다.

일주일에 코드 몇만줄 짜냐느니 등으로 일을 열심히했다는 바로미터로 삼는 한심한 일도 비일비재하지만, 대다수의 능력있는 프로그래머들은 적은양의 코드를 작성하면서 원하는 목표를 달성하는 일이 얼마나 가치있는지 잘 아실테니까요.

책 페이지 수도 많지 않고 (부록 제외 240여 페이지), 내용들도 쉽게 쉽게 읽히는 편인지라, C++ 코딩의 정석 만큼이나 추천드리고 싶은 책입니다.

물론, C++ 코딩의 정석은 실수 사례도 모은 느낌이고, 이 책은 코드 작성 + 코드 읽기 관점이지만 말이지요.

팀 내 가이드로 삼아도 될만큼 좋은 책입니다. 추천!

Active Record Query Interface

액티브 레코드 쿼리 인터페이스

튜토리얼만 잘 읽고 가이드만 잘 따라가도 평타를 칠 수 있는 언어! 루비….인데, 액티브 쿼리 인터페이스 읽다말고 find_by_sql을 발견 한 후, find_by_sql 위주로 작업을 했더니 몇가지 문제가 있었습니다.

현재의 세팅환경은, 다음과 같습니다.

develop : sqlite production : mysql

이렇게 쓰다보니, 특정 dbms 종속형 쿼리를 작성 했을시에 특정환경에선 동작하지 않는 기능을 만들어버리는 것이었죠.

ROR의 액티브 쿼리 인터페이스란걸 알고보니 어지간한건 직접 쿼리 안짜고 가능하더군요!!

아래는 ROR에서 지원하는 메소드 종류입니다.

  • where
  • select
  • group
  • order
  • limit
  • offset
  • joins
  • includes
  • lock
  • readonly
  • from
  • having

뭐 여타 DBMS에서도 흔히 볼 수 있는 구문들이므로…자세한 설명은 패스하겠습니다.

만약 find_by_sql을 많이 쓰고 계신다면, 가급적 액티브 쿼리 인터페이스를 쓰시는 것이 여러모로 장점이 많지 않을까 싶네요.

명령행 프로그램 이야기

내가 처음 접한 프로그래밍 언어는 Basic이 아닌, C였다.

그리고 Turbo-C 2.0이 첫 컴파일러였다.

내가 처음 샀던 C언어 서적이 터보 C 2.0을 알려주는 주황색 서적이었는데, 뭔가 시리즈 였던 기억이 난다.

그 책이 너무 설명이 어려워, 다음에 샀던 책이 바로, Turbo-C 2.0 길라잡이다.

내 기억에 이 책의 표지는 초록색이었는데, 사진도 목차도 안나와있어서이책이 맞는지는 모르겠다

여튼 당시 내가 봤던 서적에서의 “Hello, World!”는 다음과 같다.

#include < stdio.h >
void main()
{
    printf("Hello, World!\n");
}

뭐….당시엔 대다수 국내 C언어 서적이 저랬을려나…? ANSI C Programming의 번역서만이.

#include < stdio.h >
int main(int argc, char* argv[])
{
    printf("Hello, World!\n");
    return 0;
}

였던걸로 기억한다.

고작 이게 뭐 그리 중요하냐고? 이 별거 아닌 차이가, 유닉스 문화와 윈도우 문화를 가르는 발단이 되었기 때문이다.

GCC에서는 애초에 void main()이 허용되지 않는다.

유닉스에서는 명령행 프로그램 사이의 연동에 인자(int argc, char* argv[])를, 넘기고 그 결과로 exit 코드 (main의 int 리턴 타입)를 이용한다. 그래서 프로그램간의 연동이 쉽게 가능하며, 명령행 프로그램 위에 UI를 붙이는 과정이 이런 전제하에 있다. (물론, 파이프 통신이나 소켓 통신등으로 IPC를 하기도 하지만)

여러 포스팅을 보면, void mai()과 int main(int argc, char* argv[])의 차이에 대해 표준에 대한 이유를 언급하곤 하는데, 이 표준의 전제에는 프로그램 간의 상호 작용을 매끄럽게 하기 위해서라 할 수 있다.

터보-C와 MSC라 불리우는 Visual C++이 void main() 을 허용하다보니, 그렇게 만들어진 프로그램의 exit코드는 신용할 수 없다. 물론 리눅스 환경에서 개발됐고, ANSI C 표준을 따른 프로그램이라해도, 목표로한 동작을 완료하지 못했음에도 exit코드를 0이 아닌 값을 반환하는 상황도 있을 수 있으나, 대부분의 규칙을 잘 따른 프로그램들은 정상 수행에 0을 반환한다는 것을 목표로 삼고 있다.

이 반환값을 기반으로 도는 프로그램은 수없이 많다.

나는 철저히 윈도우 프로그래머였다. 그것도 클라이언트 온리 게임만 만들던 동인 게임 개발자였고.

애초에 프로그램간의 연동이 목표가 아니라, 그저 내 프로그램 하나가 수많은 기능을 담고 싶어했던, 바퀴를 다시 발명하는 것을 즐겼던(?) 그저 흔한 프로그래머였음은 물론이고. 심지어 게임 개발할 때마다 전용 툴까지도 만들었던…흑역사를 품고 있다.

내가 윈도우 쉘 연동으로 생산성을 높이게됐던 계기는, 서버 개발자가 된 후 라이브팀 업무때가 주로 그랬다. 나에게 주어진 시간은 그다지 많지 않았고, 컨텐츠를 개발하는 일 이외의 시간은 사실 잘 주어지지 않았다. 그렇다보니 자연스레 바퀴를 재발명하는건 시간이 부족하다보니 여의치않아졌다.

가능하면 기존에 있는 기능들을 잘 묶고, 명령행 프로그램 여러개의 기능을 엮는 자동화에 대한 의지가 늘게된 시기였다. 

물론 이전에도 난 게으르기 때문에, 게을러 지기 위한 기반 작업. 자동화에 대한 의지가 있었으나 그 자동화를 C++ 코드위에서 하곤 했다. FTP고, HTTP고 WIN32 API를 통해 다시 만들어, 내 프로그램 위에 얹었다.

잘 만들어져 있는 명령행 프로그램들을 엮는 일이, 얼마나 내 생산성을 높아지게 했는지 뼈저리게 느꼈다. 그와 함께 기존에 안좋았던 습관들이 조금씩 사라지게 된 계기가 됐고.

그런 깨닳음을 얻어가던 중 내가 만든 여러 프로그램들을 돌이켜봤다.

어째서 내 프로그램은 어찌 하나같이, GUI 기반으로만 동작하는가?!? GUI기반으로 동작하는건 좋다 이거야. 헌데, 다른 프로그램과 엮기 왜 이렇게 힘든거지?

내가 짠 프로그램들은 하나같이 main함수에서 return 0으로 프로그램을 종료시키고 있었다. 원하는 목표를 달성했는지 여부와는 전혀 상관없이 말이다.

기능을 재사용하는건 , 라이브러리화 해둔 코드를 재사용하는 것에 의존했을 뿐이었고.

별거 아닌거 같으면서, 큰 깨닳음을 얻고, 자동화와 각종 보조 업무등을 위해 다른 사람들이 만든 프로그램들을 엮는 작업이 늘어가면서 내가 만든 프로그램들도 그 사이에 엮을 수 있게끔  맞춰가고 있다.

아직 많이 미숙하지만 리눅스 환경에서 ROR을 운영하며 리눅스에서의 기능 재사용, 프로그램 사이의 연동의 문화, 흔히 리눅스 문화라 불리는 것들을 잘 느낄 수 있었다. 

윈도우 환경 위에서 만들어진 수 많은 메이저 프로그램이 명령행 기능을 지원하지 않는다. 

특정 프로그램에서 95%가 만족스러운데, 살짝 아쉬운 한두가지 요소 때문에, 다른 프로그램을 찾아 다녀야했던 일이 리눅스 문화라고 없는건 아니지만, 명령행 위에 UI를 얹은 많은 프로그램들은 이런 고민을 해결해준다.

이런 깨닳음이 늦게 온 원인이 윈도우 환경 위에서 프로그래밍을 해왔기 때문이라고 핑계대고 싶진 않다. 다만 GUI 환경의 문화가 프로그램 사이의 연동을 어렵게 만드는 요인이라는 생각을 갖게 됐다.

아직 능숙해지려면 멀었지만 리눅스 환경에 조금씩 적응될수록 여러가지 생각이 드는데, 그 중 명령행 프로그램에 대한 이야기는 꼭 한번 하고 싶었다.

앞으로도 윈도우 환경에 대한 감사함을 얼마나 느끼게 될지, 리눅스 환경에 대한 감탄을 얼마나 하게 될지는 잘 모르겠지만 윈도우’만’ 써왔다고 할 수 있는 프로그래머가, 리눅스 환경에 적응해가며 드는 감상을 적어보겠다. 

Ruby 변수 관련 정리

변수 접두어

  • 루비는 C언어등과 다르게, 접두어가 변수의 종류를 구분 짓는다.
  • 아래는 변수를 구분 짓는 접두어를 의미한다.
  • 내 네이밍 규칙에 따르면 모든 변수를 상수로 만드는데, 루비의 접두어 룰로 인해 나도 네이밍 습관을 루비에선 따로 쓸 수 밖에 없었다.
기호 의미
$ 전역 변수
@ 인스턴스 변수 
@@ 클래스 멤버 변수
a-z_ 지역 변수
A-Z 상수

전역 변수

  • 루비에서 미리 정의해두고, 스크립트 작성에 도움이 되게 지원하는 변수들입니다.
  • 적절히 사용하시면 아주 유용합니다!
기호 의미
$! 마지막 에러 메시지
$@ 에러 위치
$_ 가장 최근에 gets로 읽은 문자열
$. 코드의 줄 번호
$& regexp로 마지막에 매칭된 문자열
$~ the last regexp match, as an array of subexpressions
$n n번째로 매칭된 문자열
$= case-insensitivity flag
$/ input record separator
$\ output record separator
$0 실행 프로그램 이름
$* 명령 행 인자들
$$ 프로세스 아이디
$? 최근 실행한 자식 프로세스의 종료 번호
ARGV[n] n 번째 명령 행 인자
$DEBUG 디버깅 메시지 -d를 키면 활성화
$stderr 표준에러
$stdin 표준입력
$stdout 표준출력
$: 스크립트가 로드한 모듈 이름들
ENV 시스템 환경 변수를 저장하고 있는 해쉬. 환경 변수 값을 변경할 수 있으며, 변경사항은 해당 프로세스 와 자식 프로세스에만 반영된다.