왠 낚시제목 같은 RSS피드가 떡하니 날아와서 내 썬더버드의 목록을 채우고 있길래, 파닥파닥 한번 해주는셈 치고 글을 읽어보았는데, 왠걸, 글 내용이 무척 좋다.

뭔 글이고 하니 요새 zdnet에서 하고 있는 슈퍼개발자의 길이라는 연재 기사다.

개발 2년이 갓 넘은 피덩어리인데, 찔끔찔끔 지칠때가 있다. 특히 요새같이 개강을 한다든지 해서 환경이 바뀌면 부쩍 우울해지고 꿀꿀해지는데 연재에 나오는 슈퍼개발자들 이야기를 들어보면 재미있다.

특히 좋았던글 몇개를 소개하자면,

나눔과 교육으로 날아라 : ... 그들(슈퍼개발자들)은 잘 안 풀리는 문제를 만나도, 씨익 웃어주는 여유와 함께 쉬는 시간에 사람들과 커피 한잔 하면서 최신 개그라도 하나쯤 흉내 내어 보는 그런 긍정적인 자세와 사고를 가지고 일을 한다.

기본기 없는 고수는 없다, 코드 리뷰 (Code Review)를 통해서 널리 알려라

프로그래밍을 즐기는법 : ... 이미 진행 중인 프로젝트가 있다면, 거기에 약간의 새로움을 가미해보길 바란다. 그렇다면 더 즐겁게 일할 수 있을 것이다. 물론 그 새로움이란, 자기계발과 프로젝트에 모두 도움이 되는 것으로 선택하는 것이 좋다. 그리고 작은 목표(기왕이면 측정 가능한 것)도 도움이 된다. 예를 들면 코드 품질을 나타낼 수 있는 몇 가지 방법(Code Metrics)을 적용할 수 있을 것이다.

본디, 프로그래밍은 재미있는것이다. 그런것이다.
C++ 템플릿 특화중에 특이한 사실중에 하나는, Inner Class의 완전특화가 C++표준이 아니라는것이다. 그래서 Inner Class를 완전특화에서 사용하면 gcc에서는 컴파일이 안된다.

회사에서 매주 월요일 점심시간 짜투리에 CodeDojo 라는 이름으로 Programming Challenges 책의 문제를 푼다.(하긴, 이것도 월요일날 학교를 가게되어서 앞으로는 하지 못할것 같다.) 문제들은 전형적인 ACM 문제유형으로 인풋이 주어지면 문제를 해결한 적절한 아웃풋을 만들어내야하는 식이다.

참여횟수가 늘어나면 인풋을 적절히 일반화를 해보고자 하여 템플릿을 사용하게 되었는데, 이를테면 인풋의 값을 string 형태로 보관한다거나 혹은 int 형태로 보관할수 있게 만들고자했다. 여기에 보통 '0 0'를 입력하면 입력을 종료한다는 조건이 붙는데 stirng형이면 zero가 char형의 '0'가 될 것이고 int 형이면 zero가 int형의 0이 되도록 하기 위해 템플릿 완전(명시적) 특화를 사용하려 하였다.

그런데 VS에서 멀쩡히 컴파일되던 녀석이 테스트 봇 - gcc를 사용한다 - 에서 컴파일이 안되어서 여기저기 알아보니 클래스 내의 Inner Class는 템플릿의 명시적 특화가 '확장이 아닌 표준을 따르면' 지원되지 않는덴다. 그래서 꼼수로 생각한게 부분특화를 사용하고 템플릿 인자에 기본인자를 사용하였더니 gcc나 VS에서 문제없이 컴파일이 되었다.

아래는 이렇게 작성된 부분특화용 Inner Class.

(Language : cpp)
//////////////////////////////////////////////////////////////////////
//
// Input Processor Interface
//
//////////////////////////////////////////////////////////////////////

class InputProcessor
{
public:

    //  break type
    enum BREAK_CONDITION
    {
        BREAK_EOF = 0,
        BREAK_ENTER,
        BREAK_ZERO,

        BREAK_TOTAL
    };

    // 생성자 / 소멸자
    InputProcessor(BREAK_CONDITION _eBreak) { eBreakCondition_ = _eBreak; }
    virtual ~InputProcessor(){}

    // 인터페이스
    virtual void  process()              = 0;
    virtual int   getDataIndexCount()    = 0;
    virtual const std::vector<int>* getDigits(int _nIndex)   = 0;
    virtual const std::vector<std::string>* getChars(int _nIndex)   = 0;

    // Test를 위한 함수
    virtual void addTestInput(const char* pSzTestData) = 0;

protected:

    // break condition
    BREAK_CONDITION  eBreakCondition_;

    // data manipulator
    // 로컬 템플릿 클래스는 명시적 특화가 허용되지 않고 부분특화만 허용됨. 그에 대한 꽁수
    // cf. 비주얼스튜디오 확장으로 로컬클래스의 명시적 특화가 허용된다.
    template< typename T, typename PartialSpecialization = bool >
    struct SDataManipulator{};
    template< typename PartialSpecialization >
    struct SDataManipulator< int, PartialSpecialization >
    {
        static int convertData(char* _pSzData){ return atoi( _pSzData ); }
        static const int getZero(){return 0;}
        static const std::vector<int>* getDigits(
                std::vector< std::vector<int>* >& _refVecData,
                int _nIndex)
        {
            return _refVecData[ _nIndex ];
        }
        static const std::vector<std::string>*  getChars(
                std::vector< std::vector<int>* >& _refVecData,
                int _nIndex)
        {
            return 0;
        }
    };
    template< typename PartialSpecialization >
    struct SDataManipulator< std::string, PartialSpecialization >
    {
        static char* convertData(char* _pSzData){ return _pSzData; }
        static const char* getZero(){ return "0";}
        static const std::vector<int>* getDigits(
                std::vector< std::vector<std::string>* >& _refVecData,
                int _nIndex)
        {
            return 0;
        }
        static const std::vector<std::string>*  getChars(
                std::vector< std::vector<std::string>* >& _refVecData,
                int _nIndex)
        {
            return _refVecData[ _nIndex ];
        }
    };

    // multiline data
    template< typename T >
    struct SInputData
    {
        // data container
        std::vector< std::vector<T>* > aVecVecData;

        // get data
        void processData(BREAK_CONDITION _eBreak, const char* _pSzManualData )
        {
            char buf[512];
            bool bLoop = true;
            while( bLoop )
            {
                if( _pSzManualData == 0 )
                {
                    // 라인으로 입력받기
                    if( gets( buf ) == 0 )
                        return;
                }
                else
                {
                    strcpy(buf, _pSzManualData);
                    bLoop = false;
                }

                std::vector<T>*  pVector = new std::vector<T>;
                char* pSzEach = strtok(buf, " ");
                while (pSzEach!= 0)
                {
                    pVector->push_back(
                                SDataManipulator<T>::convertData( pSzEach )
                    );
                    pSzEach = strtok(0, " ");
                }

                // Zero Break
                if( _eBreak == BREAK_ZERO && pVector->size() == 2 )
                {
                    if( (*pVector)[0] == SDataManipulator<T>::getZero() &&
                        (*pVector)[1] == SDataManipulator<T>::getZero() )
                    {
                        delete pVector;
                        return;
                    }
                }

                aVecVecData.push_back( pVector );

                // Enter Break
                if( _eBreak == BREAK_ENTER )
                    return;
            }
        }

        // clear data
        void clearData()
        {
            int nSize = (int)aVecVecData.size();
            for( int i = 0; i < nSize; ++i)
            {
                delete aVecVecData[i];
            }
            aVecVecData.clear();
        }
    };

};


//////////////////////////////////////////////////////////////////////
//
// Input Processor Implimentation
//
//////////////////////////////////////////////////////////////////////

template< typename T >
class InputProcessorImpl : public InputProcessor
{
public:
    InputProcessorImpl(BREAK_CONDITION _eBreak) : InputProcessor(_eBreak) {}
    virtual ~InputProcessorImpl(){ aData_.clearData(); }

    virtual void process()
    {
        if( aData_.aVecVecData.empty() == false )
                aData_.clearData();
        aData_.processData( eBreakCondition_, 0);
    }

    virtual const std::vector<int>* getDigits(int _nIndex)
    {
        if( _nIndex < 0 ||
            aData_.aVecVecData.empty() ||
            _nIndex >= getDataIndexCount() )
            return 0;

        return InputProcessor::SDataManipulator<T>::getDigits(aData_.aVecVecData, _nIndex);
    }

    virtual const std::vector<std::string>* getChars(int _nIndex)
    {
        if( _nIndex < 0 ||
             aData_.aVecVecData.empty() ||
              _nIndex >= getDataIndexCount() )
            return 0;

        return InputProcessor::SDataManipulator<T>::getChars(aData_.aVecVecData, _nIndex);
    }

    virtual int getDataIndexCount()
    {
        return (int)aData_.aVecVecData.size();
    }

    virtual void addTestInput(const char* pSzTestData)
    {
        aData_.processData(InputProcessor::BREAK_EOF, pSzTestData );
    }

private:
    InputProcessor::SInputData< T > aData_;
};

아래는 보너스로, 위의 인풋프로세스를 이용하여 푼 LCD문제. (물론 봇테스트도 통과, 최적화는 되어있지 않다)
http://www.programming-challenges.com/pg.php?page=viewsubmission&subid=211071

아래는 위의 일반화된 인풋이전에 인풋을 나누어 사용했을때 풀었던 문제 MineSweeper.
http://www.programming-challenges.com/pg.php?page=viewsubmission&subid=200889

Trackback Logs

  • http://blog.maiet.net/xe/1386/e77/trackback

개학하다

일상 이야기 2008/09/02 03:39
개학했다. 새로운 학기가 시작되었다. 꿀꿀한 비와함께...

왠지 첫 수업에 나가기가 싫더라. 기업영어라는 과목인데 내가 싫어하는 영어과목이기도 하거니와, 비가오는날은 움직이기가 더 싫어질뿐만 아니라, 첫날이니 뭐 한거 없겠지라는 생각등등... 이불을 껴안고 걍 자버렸다.

자는 김에 쭉~ 자서 2교시 수업도 못들었다. 심리학 수업인데 젠장.. 시계를 보며 가야지 가야지 하는데 안가도 되는 이유가 수백가지는 떠오르더라..

몸이 허하거나 하기싫은일에 쌓여있을때는 군대꿈을 꾸곤하는데, 빌어먹게도 군대꿈을 꿨다. 이등병으로 다시들어갔는데, 젠장... 까라니까 꿈에서 잘도 까더라.. 망할. 군대에서 이등병때 굽신굽신했던짓을 꿈속에서 다시하니까 기분 정말 더럽더라.

언제 푹 쉬나 달력을 들춰보는데 추석이 토일월이더라.. 젠장할..

수업을 빼먹으면 기분 더러워지는걸 알면서도 수십번을, 수년을 반복해도 나아지지 않는다. 그렇게 술에 물탄듯 물에 술탄듯 세월을 처먹는다. 나 어쩔라고 이럴까.....

꾸불꾸불한 날, 집이라도 청소하면 기분이라도 나아졌는데 지금은 그럴 집도 없다. 지갑에는 천원짜기 지폐한장있고 카드결제시에 쿠사리 안주는 제과점에서 빵을 7000원어치 카드긁어 사먹어도 기분이 나아지지 않는다.

무력하게 잠만오고..

말없이 술 받아주던 군대친구녀석들이 그립다. 하기사 사회에서 그녀석들은 예전의 그녀석들하고는 쪼금 달라 선뜻불러내서 술받아달라고 징징대기도 뭐하고..

뭐했나 싶기도 하다. 그러고 보면 근래에 친구 안사귄지도 꽤 된것 같다. pok씨로 부르는 동료에게 갑자기 친구먹자고 하기도 그렇고 저 사람이 나를 도구로 보는지 인간으로 보는지 어렴풋이 보이면서 내 속내를 드러내기도 뭐하고.. 나를 치장하고 나를 감추는게 나한테 편하다는걸 깨달은지 오래지만, 오늘같이 우울한날 위로한답시고 까불어대던 후배녀셕들이나 있는돈 없는돈 긁어 술 사주던 선배들 같은 사람들을 더이상 못만든게 아쉽다.

기분 꿀꿀한데, 일주일간 쫙 짼다고 나아질것도 같지 않고 어차피 기분만 꿀꿀해지겠지..
정상궤도로 올릴수 있는 판이나 짜봐야겠다.
TAG , , 주저리
NetBeans용 jVi의 감동으로 인해 이클립스내에서 vi키를 쓸수 있는 플러그인들을 sf에서 찾아보았다.

viPlugin은 0.2버전이후에 상용으로 돌아섰는데, 구버전은 쓸만하지 않다.
eclipseviplugin은 오픈소스중에서 그나마 쓸만한 녀석인데, 'v', 'shift+v'가 안된다. 으악.
vimeclipse는... 문제는 이녀석이다.

뭔가 있어보이는 이녀석은 이클립스안에 gvim을 임베이딩 해준다. 그리고 그게 전부다. 이렇게 하면 OutLine이나 뭐나 그런거 안된다. 이클립스 자동 채워주기 요런거 안된다. 그냥 임베이딩만 해준다. 이거 깔라고 잘쓰고 있던 vim 밀고 최신의 cream을 깔았는데 허무했다.

삽질의 하이라이트는 bufexplorer였다.
최신의 cream을 깔면서 플러그인들도 업그레이드 했는데 요 bufexplorer의 '\bs', '\bv'기능이 안되는거다. 물론 일반적으로 \be를 썼지만, 안되니까 신경질이났다.

가장 쉬워보이는, cream에서 다시 예전의 gvim7.1로 다운그래이를 해봐도 안되고 해서 bufexplorer 최신 버전이 아닌가.. 싶어서 스크립트 홈페이지에 가보니까

bufexplorer.zip 7.1.2 2007-11-07 6.0 jeff lanzarotta This is a MAJOR update.
* Added handling of tabs. (Dave Larson)
* Removed \bs and \bv commands because these are easier for the user to create horizontal and vertical windows. (Dave Larson)
* Fixed jumplist issue spotted by JiangJun.
* Went back to using just a plugin file, instead of both an autoload and plugin file. The splitting of the file caused issues with other plugins. So if you have a prior version of bufexplorer that has an autoload file, please remove autoload\bufexplorer and plugin\bufexplorer before installing this new version.
* Fixed E493 error spotted by Thomas Arendsen Hein.
* Minor cosmetic changes.
* Minor help file changes.

음? 이 뭥미? 정말 말그대로 완전 삽질했다.

그나마 삽질하면서 몇가지 건진게 있는데, 일단 글꼴문제.
http://gooom.kr/16
윈도우에 내가 원하는 글꼴셋을 묶는 기능이 없는지 궁금했는데 있더라. 아이좋아.

그리고 인코딩 문제.
http://kldp.org/node/32987
euc-kr 파일과 utf-8 파일을 구분하여 읽기중에 이렇게 간단한 방법이 있었다니!

이 두가지 해결한거에 3시간 삽질을 위로해야겠다
나에게 있어서, 보고 감동한 소스들 중에는 '타입리스트'가 항상 들어간다.

참여중인 프로젝트에서 타입리스트를 사용하여 'enum'의 사용량을 줄여보려고 시도해본적이 있었다. 그러니까, enum대신에 TL::IndexOf< T_Typlist, MyClass >::value 등의 값을 사용하려고 했었다. 이렇게 하면, 사용하려는 클래스군 - 아마, state 나 strategy pattern 들의 구현클래스들이겠지 - 이 추가될때 마나 T_Typelist 에 추가되는 클래스만 적어주면 된다.

적용을 해봤는데, 다른 분들의 반응이 영 탐탁치 않다. 아마도 익숙한 enum을 나두고 왜 굳이 생소한 타입리스트를 사용하는지에 대한 불만의 표출이었으리라.

곰곰히 자기반성을 해보니, 이건 '잘난체' 외에 아무것도 아니라는걸 느꼈다. 나에게 반문해보니 enum과 class의 중복등록이 정말 나빠서 없애려고 했던것 보다는 타입리스트를 프로젝트에서 써보고 싶었던것이 더 크게 작용한것같다. 이러한 행동은 팀워크를 깨뜨리고 분위기를 싸~ 하게 만드는, 얻는것은 없고 잃는것은 많은 '못된'짓이다.

팀은 새로운것을 즐기는 사람, 코딩보다는 결과물을 더 즐기는 사람, 익숙한것들 활용하여 문제를 해결하려는 사람등 다양한 사람들로 구성된다. 그리고 이러한 팀원들의 합동작업으로 최종 결과물이 나온다. 새로운것을 즐기는 사람이 짠 코드를 익숙한 것을 활용하여 문제를 해결하려는 사람이 유지보수를 맡게 되는 경우도 허다하다. 그래서 다른 팀원이 공감할수 없는 코드은 '못된'코드다.

다른분들에게 생소한 메타프로그래밍은, 정말로 쓰이면 크게 좋을때, 외부 노출을 최대한 적게 하여 만들어야 한다. 그리고, 메타프로그래밍을 사용했을때의 장점을 충분히 납득시켜야 한다.

결론? 메타프로그래밍은 코드 밑단의, 지하세계를 지키는일에 사용하자.