2014년 1월 15일 수요일

C++11 Feature 정리 - nullptr, Range-for statement, override and final

Code Project의 "Ten C++11 Features Every C++ Developer Should Use"를 기반으로 
C++11 feature들에 대해서 초보적인 시각에서 간단히 정리 함.

http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer


[nullptr]

일반적으로 macro로 사용하던 NULL을 명시적으로 nullptr로 정의 한것 같다.
이전에 사용하던 macro들의 값은 0 이거나 (void*)0 이었는데
0일 경우는 pointer가 아닌 type에도 사용가능하고  (void*)0일 경우는 C++에서 conversion
 문제가 발생 될 수 있어 
pointer형에만 사용가능한 nullptr를 사용하는 것이 가독성 측면과 불필요한 conversion으로 인한 문제를 피할 수 있을 것 같음.

void foo(int* p) {}

void bar(std::shared_ptr<int> p) {}

int* p1 = NULL;
int* p2 = nullptr;   
if(p1 == p2)
{
}

foo(nullptr);
bar(nullptr);

bool f = nullptr;
int i = nullptr; // error: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type

[Range-for statement]

for each 컨셉 문법이고 boost library나 java같은 곳에서는 이미 사용하고 있던 문법이라 이애하기는 쉬울듯.
사용 대상은 array나 iterative한 data structure, begin()/end()를 overloading한 object라면 사용가능하다. (Range-for statement는 array를 사용하듯 T v[N]에 대해 v+N 과 같이 loop을 진행한다.)
이 구문을 실행하기 위해서 컴파일러는 : 다음에 명시된 것에 대해서 iterator를 찾고자 v.begin() / v.end() 아니면 begin(v) / end(v)를 호출하거나 scope 상에서 begin() / end()가 있는지 확인하며 없다면 에러를 리턴한다.


std::map<std::string, std::vector<int>> map;
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
map["one"] = v;

for(const auto& kvp : map) 
{
  std::cout << kvp.first << std::endl;

  for(auto v : kvp.second)
  {
     std::cout << v << std::endl;
  }
}

int arr[] = {1,2,3,4,5};
for(int& e : arr) 
{
  e = e*e;
}



[Override and final]

위 code project article의 저자가 말한 듯이
C++에서의 overriding을 사용할 때 문제가 되는 부분은
 . virtual keyword를 반드시 사용해야 한다는 것과
 . 자식 class에서의 virtual keyword 사용의 optional 인것으로 인한 가독성 문제 때문에 

overriding 될때는 부모 class에서나 자식 class에서나 모두 keyword를 항상 붙이기도 했었음. 
하지만 C++ programming language 책에서 Bjarne Stroustrup 아저씨께서는 virtual keyword를 자식까지 붙이는 것을 추천하지 않으시고 명시적으로 overriding function을 알리려면 'override'를 사용하라고 하고 있음.

위 Article에서는 저자가 virtual keyword의 문제점으로
 . function argument가 다를 경우 overloading으로 처리되어 부모 class 타입에서 overloading된 function을 호출 하여도 호출되지 않는 점과 
 . overriding된 경우에서도 const keyword의 다름으로 호출되지 않는 문제점을 말하고 있음.

* 'override'는
- override된(prototype 같음) virtual function에 대해서 사용 가능
- override는 final과 같이 contextual keyword이다.
 . override를 정의하기 전에 이미 C++로 구현된 수많은 코드들에서 사용되고 있었을 것이므로.. 기존 코드 maintenance 문제로 reserved keyword로 처리하지 못했을 것이다.
- override는 class 내에서만 사용 가능하다.

 class Derived : public Base {
    void f() override;
    void g() override;
 };

void Derived::f() override{  // Error

}

void g(){ // OK

}

- virtual은 prefix이고 override는 suffix이다.
 . void f(int) const noexcept override; // ok
 . override void f(int) const noexcept; // error
 . void f(int) override const noexcept; // error


http://en.cppreference.com/w/cpp/language/override
struct A
{
    virtual void foo();
    void bar();
};
 
struct B : A
{
    void foo() const override; // Error: Has a different signature from A::foo
    void foo() override; // OK: base class contains a virtual function with the same signature
    void bar() override; // Error: B::bar doesn't override because A::bar is not virtual
};


그리고 final은 해당 함수를 더이상 overriding 하지 않겠다는 의미로 사용하면 되지만
virtual 함수 사용 시 발생되는 불필요한 performance overload (Bjarne Storoustrup께서는 25% 정도라고 함.)를 제거하고자 하는 목적으로 사용하기도 함.

사용 법은 override와 마찬가지임. 

class B 
{
public:
   virtual void f(int) {std::cout << "B::f" << std::endl;}
};

class D : public B
{
public:
   virtual void f(int) override final {std::cout << "D::f" << std::endl;}
};

class F : public D
{
public:
   virtual void f(int) override {std::cout << "F::f" << std::endl;}   // error
};



















2014년 1월 3일 금요일

C++11 Feature 정리 - auto, trailing(suffix) return type

이전 블로그에서 이전 함 (원본 글 2014/01/03 작성)

일단 Code Project의 "Ten C++11 Features Every C++ Developer Should Use"를 기반으로 
C++11 feature들에 대해서 대충 정리 함.


[auto - deduction of a type from an initializer]

Deducing the type of variable from its initializer expression

말 그대로 생성자를 기반하여 변수의 type을 추정하게 하는 키워드. compile 시간에 사용 가능.
위 링크에서의 예제를 보면 바로 이해가 될 것임.
auto x = 7;                       // int 
auto x = expression;         // expression의 결과값의 type

auto v2 = 'c'                    // char
auto v3 {f()}                    // f()의 리턴값의 type
auto x0 {}                       // error
auto x3 {1,2,3}                // 3개의 값을 가진 int list

템플릿 인자를 다룰 때 유용하게 사용되어짐.

C++11
  template<class T> void printall(const vector<T>& v)
{
for (auto p = v.begin(); p!=v.end(); ++p)
cout << *p << "\n";
}

In C++98, we'd have to write

template<class T> void printall(const vector<T>& v)
{
for (typename vector<T>::const_iterator p = v.begin(); p!=v.end(); ++p)
cout << *p << "\n";
}

하지만 단순히 간단히 타이핑 하는 것으로 끝나는 것이 아니라 
템플릿 인자를 사용해서 프로그래밍할 경우 좀 애매한 경우에 유용하다.
템플릿 인자를 그대로 사용할 때는 괜찮지만 템플릿 인자들을 연산해서 나오는 결과가 무엇인지 몰라 구체화 하지 못할 경우일때가 하나의 예이다.

 template<class T, class U> void multiply(const vector<T>& vt, const vector<U>& vu)
{
// ...
auto tmp = vt[i]*vu[i];
// ...
}

그리고 해당 링크에서 보면 auto feature는 1984에 개발되었지만 C compatibility 문제로 인해서 제외가 되었었다고 함. 하지만 이전 auto의 개념("this is a local variable")은 지금과 다르다고 하다. (하지만 난 history도 C++98도 몰라서.. 자세히는 모르겠다.)

추가로 trailing(suffix) return type 예를 codeproject 링크에서 보여주고 있음.

 template <typename T1, typename T2>
auto compose(T1 t1, T2 t2) -> decltype(t1 + t2)
{
   return t1+t2;
}
auto v = compose(2, 3.14); // v's type is double

trailing(suffix) return type 관련해서 사용되게 된 이유를 ibm 블로그 글에서 설명이 잘 되어 있음.

decltype()을 사용하여 a*b type으로 선언 하여 사용하고자 
왼쪽의 예를 오른쪽 처럼 바꿔서 사용할 경우 
컴파일러의 parsing 순서 상(왼쪽에서 오른쪽으로) a,b의 type을 확인하지 못해서 (a*b를 확인할 때 a,b가 선언되어 있지 않아서) 에러가 발생된다.

 template<class T,class U>
U mul(T a, T b){
return a*b;
}
 template<class T>
decltype(a*b) mul(T a, T b){
return a*b;
}

그래서 이런 문제를 해결하고자 사용 하는 방법이 trailing(suffix) return type임.

 template<class T>
auto mul(T a, T b) -> decltype(a*b){
return a*b;
}

위의 feature들을 사용하면 아래와 같이 아무런 type을 지정하지 않고도 코딩이 된 예제가 나올 수 있음. 

 #include <iostream>
using namespace std;
template<typename T1, typename T2>
auto sum(T1 & t1, T2 & t2) -> decltype(t1 + t2){
return t1 + t2;
}
int main(){
auto i = 1;
auto j = 1.3;
auto k = sum(a,b);
cout << c << endl;
}



[참고]

- constexpr

 변수의 값이나 함수의 결과값을 constant 속성을 부여하여 compile 시에 사용될 수 있도록 함.
 아래 예제에서 보면 factorial, conststr, countlower함수 들이 constexpr로 선언되어 compile 시점에 out1, out2의 template인자로 사용되었음.
 #include <iostream>
#include <stdexcept>
 
// constexpr functions use recursion rather than iteration
constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n-1));
}
 
// literal class
class conststr {
    const char * p;
    std::size_t sz;
 public:
    template<std::size_t N>
    constexpr conststr(const char(&a)[N]) : p(a), sz(N-1) {}
    // constexpr functions signal errors by throwing exceptions from operator ?:
    constexpr char operator[](std::size_t n) const {
        return n < sz ? p[n] : throw std::out_of_range("");
    }
    constexpr std::size_t size() const { return sz; }
};
 
constexpr std::size_t countlower(conststr s, std::size_t n = 0,
                                             std::size_t c = 0) {
    return n == s.size() ? c :
           s[n] >= 'a' && s[n] <= 'z' ? countlower(s, n+1, c+1) :
           countlower(s, n+1, c);
}
 
// output function that requires a compile-time constant, for testing
template<int n> struct constN {
    constN() { std::cout << n << '\n'; }
};
 
int main()
{
    std::cout << "4! = " ;
    constN<factorial(4)> out1; // computed at compile time
 
    volatile int k = 8; // disallow optimization using volatile
    std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time
 
    std::cout << "Number of lowercase letters in \"Hello, world!\" is ";
    constN<countlower("Hello, world!")> out2; // implicitly converted to conststr
}

- decltype()
  초기값이 없어 type을 추론하지 못할 경우 변수나 표현식의 declared type을 사용하여 선언할 수 있게 해주는 keyword

 #include <iostream>
 
struct A {
   double x;
};
const A* a = new A();
 
decltype( a->x ) x3;       // type of x3 is double (declared type)
decltype((a->x)) x4 = x3;  // type of x4 is const double& (lvalue expression)
 
template <class T, class U>
auto add(T t, U u) -> decltype(t + u); // return type depends on template parameters
 
int main() 
{
    int i = 33;
    decltype(i) j = i*2;
 
    std::cout << "i = " << i << ", "
              << "j = " << j << '\n';
 
    auto f = [](int a, int b) -> int {
       return a*b;
    };
 
    decltype(f) f2{f}; // the type of a lambda function is unique and unnamed
    i = f(2, 2);
    j = f2(3, 3);
 
    std::cout << "i = " << i << ", "
              << "j = " << j << '\n';
}


- trailing(suffix) return type

간단히 말하면 함수의 뒷부분에 함수의 리턴 값의 type을 명시하는 것이다.(2)
일반적으로는 함수의 처음에 리턴값의 type을 명시하는 prefix return type을 사용함(1)

(2)예: (1) string to_string(int a);       (2) auto to_string(int a) -> string;

설명은 위를 참고하고 Ibm blog 글에서 아래와 같은 예제를 보여줌..
가독성 측면에서도 유용하게 사용될 수 있을 것으로 보임.
int형 템플릿 인자를 가지는 tmp class를 리턴하는 함수 포인터인 foo를 
trailing return type으로 표시할 경우 아래와 같다는..

 template <class T> class tmp{
public:
int i;
};
tmp<int> (*(*foo())())() {
return 0;
}
 template <class T> class tmp{
public:
int i;
};
auto foo(<wbr />)->a<wbr />uto(<wbr />*)()<wbr />->tm<wbr />p<in<wbr />t>(*<wbr />)()<wbr />{
return 0;
}

2014년 1월 2일 목요일

(Link) C++11 관련 링크 정리

이전 블로그에서 이전 함 (원본 글 2014/01/02 작성)

C++11 in Wikipedia

C++ 표준화 관련 뉴스와 상태를 확인할 수 있는 사이트

C++11 FAQ by Bjarne Stroustrup

Ten C++11 Features Every C++ Developer Should Use

C++11 기능(최신 C++) with Visual Studio 2013

2014년 1월 1일 수요일

Teach Yourself Programming in Ten Years, Peter Norvig

이전 블로그에서 이전 함 (원본 글 2014/01/01 작성)

Teach Yourself Programming in Ten Years, Peter Norvig

이전 오픈소스 책에서 Interviewee가 묻는 질문 중 하나여서 찾아봄.

처음에 한 책(3일안에 C++ 배우기)을 언급하며 
여러 분야에서 전문가가 되기 위해서는 오랜 시간 (보통 10년)이 걸리는데 
우리는 프로그래밍 기술을 배우는데 왜 이리 급하냐라고 묻고 있음.

그리고 프로그래머로 성공?(그냥 좋은 프로그래머가 되는 것이라고 보고 싶다.)하기 위한 방법을 소개하고 있음. 내용과 개인적인 생각을 함께 정리하자면 다음과 같다.

- 10년 혹은 10000 시간 동안 프로그래밍에 관심을 가지고 뭘 해봐라.
 : 대학생때에 이렇게 해보지 못한걸 좀 후회한다.. 휴학을 하고 좀 배우긴 했지만 학부때 놀아버린 것을 만회하진 못했다. 그리고 CS 전공 과목을 모두 숙지하지 못한것도..

- 프로그램을 만들어보는 것이 최고의 배움 방법이다.

- 프로그래머들과 교류하거나 다른 코드를 공부하라.
 : 좀 이르긴 하겠지만 대학생 때 Open Source community에 참가하는 것도 좋겠다는 생각이 들고 아니면 소프트웨어 멤버쉽 같은 것도 좋고.. 다만 방산이나 취직은 좀 일단 학부 때 취직하면 전공은 소흘해 지니까. 

- Computer Science 학위는 좀더 다양한 일을 선택할 수 있게 해주고 좀 더 깊은 지식을 얻게 해주겠지만 직업을 가지고 일을 하면서도 같은 경험을 얻을 수 있다. 
 : The New Hacker's Dictionary 책을 언급하며 말함.

- 다른 프로그래머와 함께 일을 하며 최고의 프로그래머가 되어 프로젝트를 이끌어 보고 최악의 프로그래머가 되어 리더가 어떻게 하는 지를 배우라.

- 다른 프로그래머를 이어서 일을 해보라. 다른 사람의 코드를 도움 없이 이해하고 유지보수를 경험하며 어떻게 하면 유지보수성이 높은 프로그램을 설계할지를 고민해 보라.

- 적어도 여섯개 정도의 프로그래밍 언어를 배우라, 예시... class abstraction 지원 언어(Java, C++ 같은)나 functional abstraction 지원 언어(Lisp, ML 같은), syntactic abstraction 지원 언어(Lisp 같은), declarative specifications 지원 언어(Prolog, C++ templates 같은), co-routines 지원 언어(Icon, Scheme 같은), parallelism 지원 언어(Sisal 같은) 많기도 하다..

- Computer Science에서 Computer가 내부적으로 어떻게 동작하는 지를 생각해 보라. 
 : 내 생각으로는 이건 프로그래밍 뿐만 아니라 내면?의 부분에도 관심을 가지고 이해하라는 얘기 인듯. 사실 application 개발자로 살면서 middleware나 operating system 이면을 들여다 보지 못하면 performance를 이해할 수 없게 되는 것과 같이 그런 부분을 지적한 것이 아닌 가라는 생각.

- 프로그래밍 언어 표준화에 참가해보라. ISO C++ 표준을 만드는 것이나 내부적인 coding style을 정하는 것도 상관없다. 다만 다른 사람들이 생각하는 언어와 왜 그렇게 생각하는지에 대해서 경험하라.

- 프로그래밍 언어의 표준화(표준화 보다는 규칙이나 룰이 아닐까?)를 벗어나는 경험을 해보라. 


그리고 마지막으로 Fred Brooks의 에세이인 "No Silver Bullet"에서 언급된 Great software designer를 찾는 세가지 방법을 소개하며 누구나 great software designer가 되는 자질은 가지고 있으나 다만 조급함을 버리고 지속적으로 연습하는 노력이 필요한 것이다라고 다시 한번 말하고 있다.. 

자신이 good software designer가 아닌 great software designer가 되고자 한다면 아래의 방법을 뒤집어 생각하고 지속적인 노력이 필요할 것으로 보인다. (본인은 grate software designer가 모두를 먹여 살린다에 동의하지는 않는다.)

1. 신속하고 체계적으로 top designer를 찾으십시요.
 : 주변의 능력있는 개발자(wannabe?)를 찾고 영향을 받는게 중요하겠음.

2. 해당 인력의 경력을 세심히 관리하고 가능성을 개발할 수 있는 Career mentor를 지정하라.
 : 멘토까지는 아니더라도 선배니 동종 업계에서 근무하는 사람들의 조언을 받는 것도 방법 이겠다.

3. 인력들 간에 software designer로서 교류하며 동기부여하며 성장할 수 있는 기회를 제공하라.
 : 교류.. 사실 우물안 개구리 처럼 책만 보면서 있다간 초보적인 기술만 보게 될 것이고 능력 증대를 배가하기 위한 방법은 다른사람의 잘짜여진 코드를 보는 것임.

2013년 12월 31일 화요일

꾸준히, 자유롭게, 즐겁게 : 한국 오픈 소스 개발자들 이야기

이전 블로그에서 이전 함 (원본 글 2013/12/31 작성)

 
꾸준히, 자유롭게, 즐겁게 : 한국 오픈 소스 개발자들 이야기
송우일 저 | 인사이트(insight) | 2013년 10월



인터넷으로 도서 구매 중 저렴해서 사게 되었다.
책 내용도 모른채 그냥 IT 서적에서 가격순으로 나오는 책 중 좀 깔끔한 것으로 구매하다보니 선택이 된 책

책 사이즈가 A5 사이즈?정도로 작고 페이지도 300 페이지가 되지 않는다.
게다가 내용은 6명의 오픈소스 개발자들의 인터뷰 내용이라 대화체로 부담없이 맘 먹으면 2-3시간에 모두 읽을 수 있겠다.

인터뷰의 내용으로는 오픈소스에 관심을 가지게 된 동기, 오픈소스 커뮤니티에 참가하게 된 과정, 오픈소스를 하면서 얻게된 것들과 변화된 상황 그리고 개발자를 위한 조언들이다.
내용을 정리하면 여러가지 항목들이 나오겠지만 책의 챕터에서 대충은 느낄 수 있는 것 같다.

목차
1. 허태준 - 가장 의미 있고 즐거운 개발
2. 김정균 - 자신을 발전시키는 소중한 공부
3. 이희승 - 도전과 점진적 개선, 그리고 변화에 열린 마음
4. 류창우 - 그냥 부담 없이 취미로
5. 허준회 - 더 나은 세상을 위한 소통
6. 최준호 - 프로그래밍의 깊은 세계로 들어가는 길

책을 읽으면서 인상 깊었거나 느끼게 된것들은 1. 무엇을 하든지 일정 시간의 몰입이 필요하다. (10000시간 법칙 처럼), 2. 관심에서 그치지 않고 죽이 되든 밥이 되든 해봐야 얻는게 있다. 3. 잉여로운 삶의 필요. 4. 어릴때의 주변 환경의 중요함. 들이다.

그리고 책에 동의 하는 내용들로는

여유를 가지는 것이 중요한 것 같다. 잉여로움으로 부터 발생되거나 진보된 것들이 많은데 대다수 우리나라 IT 개발자들은 삶의 무게에 눌려 있는 것 같다.

또한 결혼 후에는 시간의 여유가 없어지는 것은 당연해서 자신이 시간을 만들어 내서 잉여로움을 즐기는 것이 중요하고 되도록이면 결혼 전, 20대, 30대 초반에 몰입을 하는 것이 필요하다는 생각임. 

후반부 최준호님께서 강조한 사용하는 solution들의 내부를 들여다 보고 어떻게 만들어 졌는지 어떻게 돌아가는지를 아는게 중요하다는 의견에 동의 한다. Network application 단 개발 중인데 사실 하부 protocol stack들이 어떻게 동작하는지를 이해하고 있느냐 없느냐에 따라서 개발 결과물의 디자인이나 성능이 결정된다고 봐도 될것 같다. 공부해야겠음... ㅜㅜ

다음은 독서 중 확인한 TODO list 들이다.

(done) - 피터 노빅 [Teach Yourself Programming in 10 Years] 찾아서 보기

- DevOps 찾아서 정리 하기

(done) - coursera(https://www.coursera.org) 에서 과정 수강하기

- 잉여 시간 만들어 활용하기 (어렵겠지만..)

(doing) - C++11 정리하기


이건 검색하다 보니 찾게 된 PT. 추천

당신의 인생에 오픈소스를 더하라 - OSCON 발표자 뒷담화
by Sungju Jin, Manager at KT / Apache Software Foundation on Oct 14, 2013

Git branching model, 코드 릴리즈를 위한 Git branch 관리 방법

이전 블로그에서 이전 함 (원본 글 2013/12/31 작성)

Git 으로 코드를 개발, 관리 하면서 
코드 릴리즈는 단순히 regression 테스트 후 tagging하고 release하는 방법을 많이 사용했었음.
프로젝트가 어느정도 규모가 될 경우에 뭔가 효율 적인 git branch 관리 방법이 필요하겠구나 생각했었는데, 어찌어찌 하다 보다 보니 아래와 같은 article이 있어 링크함.

A successful Git branching model by Vincent Driessen

이전에 관리했던 프로젝트는 소규모의 프로젝트 였던 관계로 단순히 develop, master만 유지하였음에도 불구하고 branch간 merge가 생각보다 쉽지가 않았음.

위 article에서는 아래와 같은 branching model을 추천하고 있고 다소 규모가 있는 프로젝트를 위한 것으로 생각되나 몇몇 부분은 일반적으로 필요한 내용임.


보통 사용하는 develop, master branch 외 hotfixes, feature, release branch들을 두어 관리하는 방법으로 보이고 가장 공감하는 부분은 --no-ff merge와 hotfix branch 부분이다.


--no-ff merge 관련해서는 이전에는 여러 개발자들이 private branch에서 개발을 하던 내역을 모두 develop branch에 넣게 되었는데 불필요한 commit의 history로 인해 develop branch 관리가 어려웠었다. 되도록이면 개발자에게 private branch에서 feature가 개발이 어느정도 완료 되거나 bug 수정이 완료된 상태를 develop로 merge하는 것이 권장하는 것이 좋겠다는 생각을 함.


hotfix branch 관련해서는 stable version을 release 해도 버그는 언제나 발견이 되고 급하게 수정을 하는 경우가 있었는데 그냥 다시 develop에서 수정해서 해당 내역을 master에서 cherry pick을 했었다. 이런 경우 develop branch와 master branch간의 코드 불일치로 소스코드 반영 및 테스트에서 소소한 불편함이 있었다. 별도로 master branch(release branch도 되지 않을까?)기반의 hotfix branch를 사용하면 괜찮을 것으로 생각함.

Git GUI client 추천 : 일반적으로 msysgit을 사용하겠지만 난 좀 직관적으로 보이지 않더라...
유로지만 개인 개발자에게는 공짜이고 초보자에겐 간단한 look and feel !!!