2015년 6월 30일 화요일




인간이라는 존재 자체는 경이로운 우주와 자연 앞에서는 티끌이다. 그런데 인간은 놀랍게도 그 자연을 분석하고 이해하고 있다.
물리학이라는 학문은 경이롭지만 일반인들은 넘지 못할 거대한 벽이다. 그런데 이 책은  놀랍게도 물리학을 쉽게 설명한다. 더불어 과학이 현대 사회에서 가지는 의의를 지적하고, 세계-특히 한국-의 "과학(물질) 만능주의"를 날카롭게 비판하고 일반인 가진 과학과 기술에 대한 오해를 바로 잡는다.
그러나 본질은 물리학에 대한 설명이다. 물리학의 발전 역사와 더불어 그 내용을 정말 놀랍게 쉽게 설명한다.

대칭은 이 책에서 다루는 모든 설명 중 가장 중요한 개념이다. 약간의 과장을 보태어 현대 물리학은 '대칭성'을 찾으며 발전한 학문 처럼 보인다.
물리 법칙은 장소에 상관없이 일정해야 하는 자리 옮김 대칭, 방향에 상관없이 일정해야 하는 돌림 대칭, 전하 켤레 대칭(C), 홀짝성 대치(거울 대칭, P), 시간 지남 대칭(T)은 일반적인 상식만 있어도 이해하기 쉽게 설명해 놓았다.
아쉽게도 고전역학, 상대성 이론, 양자역학에  대한 설명은 그 이론 자체가 가진 난이도 때문에 어렵다. 이런 이론 자체는 여기서 이해할 필요는 없어 보인다. 실제로 저런 이론을 책 한권으로 이해하고자 하는 것도 더 없는 욕심일 것이다.
다른 책으로 뉴튼 역학과 라그랑지안 및 헤밀토니안을 어느 정도 이해하고 상대성 이론과 양자 역학에 대해 어느 정도 지식이 있다면 쉽게 읽히겠지만, 굳이 당장 이해하지 말고 뒤에 이해해도 책의 흐름상 문제는 없어 보인다.

물리학에 고전 역학/상대성 이론/양자 역학만 있는 것은 아니다. 오히려 더 중요하다고 생각되는 것은 혼돈과 엔트로피 그리고 복잡계 현상이다.
전자의 설명을 보면, "와, 이런 것을 이해하고 발전시키는 사람이 있다니, 정말 천재다!" 라는 느낌이라면, 후자의 설명을 보면 "와, 정말 자연은 경이롭구나!"를 느낀다.

혼돈을 보면 불안정성이 자연의 성질임을 알 수 있다. 그런데 약간 더 나가면 과연 과학의 "무기"인 수학을 인간이 제대로 이해하고 있는가에 의구심이 들기도 하지만 그것은 그 나름대로 탐구하고픈 미지다.
자연을 바라보는 미시적 관점(결정론적 관점)과 거시적 관점(통계적 관점)의 차이를 이해하면, 과학이 조금 더 친숙해진다. 복잡계 현상을 보면 생명과 진화의 경이로움이 보인다.

이 모든 것을 경이롭게도 한권에 책에 담아 두었다.
사실, 나는 이책을 3번 읽었다. 처음 읽었을 때는 재미있다고 느꼈지만 뭐가 뭔지 알 지 못했다. 두번째는 내가 알고 있던 물리 지식과 비교했을 때, 정말 잘 쓴 책이라는 것을 알 수 있었다. 세번째 읽었을 때, 이 책의 내용 중 많은 부분을 그때서야 이해했고, 그때 자연은 경이롭다는 것을 느꼈다.




2015년 6월 17일 수요일

C/C++ , 좋은 프로그래밍 - 템플릿 - 4

이제 템플릿 이야기의 마지막으로 템플릿 리스트 기법과 C++11의 Variadic template을 잠시 이야기 하겠다. 사실 템플릿 리스트 기법과 Variadic template이 필요한 거의 모든 경우는 이미 구현되어 있다. 그래서 지금 이야기 하는 것을 굳이 이해할 필요는 없고, 이미 구현된 것들의 사용방법만 숙지하여도 충분하다.

첫번째로 템플릿 리스트를 이야기 해보자.
일반적으로 리스트는 이미 만들어진 데이터를 보관하기 위한 용도로 사용된다. 템플릿 리스트는 비슷한 의미로 객체 형을 보관할 수 있다. 앞서 이야기한 function, bind는 C++11이전에는 템플릿 리스트를 사용하여 구현한다.

template <class T, class U>
struct type_list
{
 typedef T head;
 typedef U tail;
};

struct null_type
{
};
단순한 템플릿이다. 두 가지 타입을 입력 받아 각각의 타입을 head, tail이라는 이름으로 재선언 한다. null_type은 아무 것도 없는 빈 클래스다. 리스트의 끝을 표현하기 위해 사용했다.

typedef type_list<int, type_list<short, type_list<double, null_type>>> type_list_sample;

위의 예시처럼, type_list를 재귀적으로 사용 가능하다. 위의 형은 컴파일 시점에 해석되어지기 때문에 런타임에는 아무런 영향도 미치지 않는다. 형은 형일 뿐이다. 그럼 위의 type_list_sample 따위의 형은 어떻게 사용할까?

template<class TLIST> struct type_group;

template<class T>
struct type_group<type_list<T, null_type>>
{
 T value;
};

template<class T, class TLIST>
struct type_group<type_list<T, TLIST>> : public type_group<TLIST>
{
 T value;
};

type_group<type_list_sample> instance;
위 구문은 type_list_sample의 실제 객체를 만드는 예제다. 템플릿의 재귀적인 전개를 이용하여 반복적으로 상속하고 최종적으로 원하는 객체를 만들어 냈다.
그런데 객체에 값을 대입하거나, 객체의 값을 읽는 행위는 어떻게 할까? 그것을 위해 몇가지 템플릿을 추가로 정의한다.

template <class TYPE_LIST, class T>
struct index_of;

template <class T>
struct index_of<null_type, T> 
{
     enum {index = -1};
};

template <class T, class TAIL>
struct index_of<type_list<T, TAIL>, T>
{
   enum {index=0};
};

template <class HEAD, class TAIL, class T>
struct index_of<type_list<HEAD, TAIL>, T> 
{
private: enum {temp = index_of<TAIL, T>::index};
public: enum {index = temp == -1? -1:1+temp};
};
조금은 복잡하지만 끊기있게 해석하기 바란다.
index_of의 일반형을 선언했지만, 정의는 없다. 아래의 명시적으로 정의된 형이 모든 형을 커버하기 때문에 굳이 구체적으로 정의할 필요는 없다.
index_of 템플릿은 아래 처럼 사용한다.

const int index_position = index_of<type_list_sample, int> ::index;

위의 예제로 type_list의 활용 방법을 보자. type_list_sample은 앞서 정의한 것 처럼, int, short, double을 그 멤버로 가지는 리스트다.
index_of의 템플릿 파라미터가 type_list와 int이기 때문에 3개의 명시적 정의 중, 2번째가 선택된다.

type_list_sample가 type_list<int, type_list<short, type_list<double, null_type>>> 임으로 index_of<type_list_sample, int> 형태는 아래 2개와 비교하면, 명확할 것이다.

struct index_of<null_type, T>
struct index_of<type_list<T, TAIL>, T>
struct index_of<type_list<HEADTAIL>, T> 
그러므로 index_position은 0이다.

index_of<type_list_sample, char> ::index 은 어떨까? 
1. 첫 번째, 두 번째는 만족하지 않기 때문에 자동적으로 세 번째가 선택될 것이다. 
 HEAD = int
 TAIL = type_list<short, type_list<double, null_type>>
 T = char

2. index_of<type_list<short, type_list<double, null_type>char>::index 역시 세 번째다
  HEAD = short
  TAIL = type_list<double, null_type>
  T = char

3. index_of<type_list<double, null_type>, char>::index는 세 번째다

4. index_of<null_type, char>은 첫 번째다.

결국 index_position은 -1이된다.

한번 더 해보자
const int index_position = index_of<type_list_sample, double> ::index;
은 위의 3번이 두 번째가 선택된다.
index_of<type_list<double, null_type>, double>::index
그러므로 index_position = 2이다.


비슷하게 다음 템플릿도 정의할 수 있다.


template <class TYPELIST, int index>
struct type_at;

template <class HEADER, class TAIL>
struct type_at<type_list<H, T>, 0> 
{
 typedef H type;
};

template <class HEADER, class TAIL, int i> 
struct type_at<type_list<HEADER, TAIL>, i>
{
 typedef typename type_at<TAIL, i-1>::type type;
};
사용 예는 다음과 같다.


typedef type_at<type_list_sample, 2>::type second_type;



간단한 사용 예제를 보자.


template<class TLIST> struct type_group;

template<class T>
struct type_group<type_list<T, null_type>>
{
 T value;

 template<int N> void* base_ptr()
 {
  return (void*)&value;
 }
 template<int N> T& get()
 {
  return value;
 }
};

template<class T, class TLIST> 
struct type_group<type_list<T, TLIST>> : public type_group<TLIST>
{
 T value;

 template<int N> void* base_ptr()
 {
  return type_group<TLIST>::base_ptr<N-1>();
 }
 template<> void* base_ptr<0>()
 {
  return (void*)&value;
 }
 template<int N> inline typename type_at<type_list<T, TLIST>, N>::type& get()
 {
  unsigned char* p = base_ptr<N>();
  return *(type_at<type_list<T, TLIST>, N>::type*)p;
 }
};

int _tmain(int argc, _TCHAR* argv[])
{
 
 type_group<type_list<int, type_list<short, type_list<double, null_type>>>> group_instance;
 group_instance.get<0>() = 1;
 group_instance.get<1>() = 2;
 group_instance.get<2>() = 100.0;
}

위의 예제는 std::tuple을 흉내내어, 심플하게 구현한 것이다. 

이런 type_list는, Variadic template이 없다면 앞서 이야기한 std::function이나 std::bind 등을 구현하기 위해서 필수적으로 사용되어야 한다.

두번째로, Variadic template을 이야기하자.
Variadic template은 템플릿 파라미터 숫자를 정하지 않은 상태로 받아 들이 수 있다. type_list의 확장 버전인 셈이다. 여기서는 소개하지 않겠지만, C++11의 Rvalue-reference와 결합하면 Perfect-forwarding이 가능해져 더욱 강력하다.

위의 예제들을 그대로 만들어 보겠다.

template<class ...> struct type_check;

template <class T, class TLIST> struct index_of2
{
 enum{ value = -1 };
};
template <class T, class ...TLIST> struct index_of2<T, type_check<T, TLIST...>>
{
 enum { value = 0 }; 
};
template <class T, class U, class ...TLIST> struct index_of2<T, type_check<U, TLIST...>> 
{
 enum { value = index_of2<U, type_check<TLIST...>>::value == -1 ? -1 : 1 + index_of2<U, type_check<TLIST...>>::value };
 
};

template <class type_check, int N> struct type_at2;

template <class T, class ...TLIST>
struct type_at2<type_check<T, TLIST...>, 0>
{
 typedef T type;
};

template <class T, class ...TLIST, int N>
struct type_at2<type_check<T, TLIST...>, N>
{
 typedef typename type_at2<type_check<TLIST...>, N-1>::type type;
};

type_check만 제대로 이해하면 type_list와 똑같다.
type_check은 N개의 임의의 템플릿 파라미터에서 가장 앞의 템플릿 파라미터를 분리 할 수 있게 해준다. 즉, type_check<...>와 type_check<T, ...>은 같은 형이기 때문에 T에 대하여 원하는 검사를 수행 할 수 있다.

사용 방법은 다음과 같다.

template<class ...T>
struct test
{
 const int n = index_of2<int, type_check<T...>>::value;
 const int total_size = sizeof...(T);
 typedef typename type_at2<type_check<T...>, 2>::type type;
};


int _tmain(int argc, _TCHAR* argv[])
{
 test<int, short, long> t;
 test<int, short, long>::type d = 100.0;
 printf("%d, %d", t.n, t.total_size);


 return 0;
}

이해하지 않아도 괜찮다. 앞서 언급했지만 응용해서 만들 수 있는 거의 대부분이 이미 STL에 구현되어 있다. 사용 방법만 익히면 된다. type_check은 std::tuple 로 이미 선언되어 있고, type_at2는 std::tuple_element로 구현되어 있다. 다만 왠 일인지 index_of2는 구현되어 있지 않다.  (혹은 내가 모르는 것일 수도 있다.)
sizeof...(T) 는 전체 템플릿 파라미터의 갯수를 구할 수 있는 신규 문법이다.

Variadic template은 vs2013이후 기본 지원하나, vs2012는 http://www.microsoft.com/en-us/download/details.aspx?id=35515 에서 다운 받은 후 추가 설치해야 하고  vs2010 이하는 지원하지 않는다.




C/C++ , 좋은 프로그래밍 - 템플릿 - 3


이제 분기문에 대해서 이야기 해보자.
네트워크 프로그램을 제작한다고 상상해보자. 보통의 처리 방법은 모든 패킷에 번호를 부여하여 그 번호에 따라 실행할 구문을 정한다.


switch(packet_type)
{
case PACKET_TYPE_1:
 ...
 break;

case PACKET_TYPE_2:
 ...
 break;
}
따위의 구문이 일반적이다. 그러나, 패킷 종류가 수십 개를 넘어 간다면 위의 형태 처럼 프로그램을 만드는 것은 바람직해 보이지 않는다.

여기서는 2가지 방법을 소개해 보려고 한다.
첫 번째는 함수 포인터를 이용하는 방법이다. 엄밀하게 사용하여야 하지만, 직관적이고 편리하다.
두 번째는 템플릿를 이용하는 방법이다. 첫번째에 비하여 훨씬 유연하지만, 잘못 제작할 경우 런타임 비용이 클 수 있다.

두가지 방법 모두 일단 STL의 map을 활용하자. map 대신, 배열을 사용할 수도 있지만 유연성을 위해서 map을 사용하겠다.

함수 포인터를 이용하는 방법은 아래와 같은 형태를 취한다.
class packet_class
{
};

void packet_function_1(packet_class* packet)
{
}
void packet_function_2(packet_class* packet)
{
}

int _tmain(int argc, _TCHAR* argv[])
{
	typedef void (*FUNCTION)(packet_class* packet);
	std::map<int, FUNCTION> function_map;

	function_map[1] = &packet_function_1;
	function_map[2] = &packet_function_2;

	packet_class* packet;
	function_map[0](packet);
}
이해하기 어려운 내용은 없다.
단지 불편한 점이 있다면, 전역 함수를 사용하기 때문에 객체 지향 위주의 프로그램에서는 객체를 넘기는 방법을 마련해야 한다는 것이 그거다.
그러면 함수 포인터를 멤버 함수 포인터로 바꾸자.

class packet_class
{
};

template<class TYPE>
class dispatch_class
{
public:
	typedef void (TYPE::*DISPATCH_FUNCTION)(packet_class* packet);

protected:
	TYPE* m_dispatch_instance;
	std::map<int, DISPATCH_FUNCTION> m_functions;

public:
	dispatch_class(TYPE* dispatch_instance) : m_dispatch_instance(dispatch_instance) {}
	void set_dispatch(int packet_type, DISPATCH_FUNCTION f)
	{
		m_functions[packet_type] = f;
	}

	void packet_dispatch(int packet_type, packet_class* packet)
	{
		DISPATCH_FUNCTION f = m_functions[packet_type];
		if (f != NULL)
		{
			(m_dispatch_instance->*f)(packet);
		}
	}
};

class dispatcher
{
public:
	void packet_function_1(packet_class* packet)
	{
	}
	void packet_function_2(packet_class* packet)
	{
	}

public:
	dispatcher()
	{
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	dispatcher packet_dispatcher;
	dispatch_class<dispatcher> dispatcher_executor(&packet_dispatcher);
	dispatcher_executor.set_dispatch(0, &dispatcher::packet_function_1);
	dispatcher_executor.set_dispatch(2, &dispatcher::packet_function_2);

	
	packet_class* packet;
	dispatcher_executor.packet_dispatch(0, packet);
}

앞서 사용한 함수 포인터와 멤버 함수 포인터의 차이점만 알고 있다면 크게 어려운 내용은 아닐 것이다.
멤버 함수 포인터 변수를 선언하는 방법과 그 변수형으로 저장된 포인터, 클래스 객체를 이용하여 멤버 함수를 호출하는 방법만 눈여겨 보기 바란다. 이 내용은 이해하기 보단 그냥 그대로 외워야 한다.

이제 템플릿을 이용해 보자. 그리고 펑크터도 이용할 것이다.
[C/C++ , 좋은 프로그래밍 - 재사용 : 템플릿 - 1]에서 미리 언급하였지만, 펑크터는 클래스 객체의 operator()(type)를 활용하여 객체를 마치 전역 함수처럼 호출하는 방법이다.
사실 멤버 함수 호출하는 방법을 굳이 operator()(type)을 쓰는 것이 꼭 필요하겠냐는 의문이 생긴다.
그러나 아래 예제를 보면 조금 이해가 쉬울 것이다. 펑크터의 장점은 함수 포인터와 여러 클래스 사이의 형의 차이를 알 필요가 없다는 점이다.


void static_function()
{
	printf("11");
}

struct functor
{
	void operator()()
	{
		printf("22");
	}
};

template<class F> 
void caller(F f)
{
	f();
}

int _tmain(int argc, _TCHAR* argv[])
{
	caller(&static_function); //case 1

	functor f;  //case 2
	caller(f);
}
즉, 값을 받아 들이는 입장에서는 객체의 특정 멤버 함수를 알 필요도 없고, 값이 객체인지 함수 포인터인지 알 필요가 없다. 주의 할 점이 이 이야기는 런타임에 관련된 이야기가 아니다. 컴파일 중, caller가 f를 어떻게 호출해야 할지 자동으로 결정한다는 이야기이다.

이제 앞선 dispatch_class를 약간 수정하자.

template<class F>
class dispatch_class
{
protected:
	std::map<int, F> m_functions;

public:
	dispatch_class(){}
	void set_dispatch(int packet_type, F f)
	{
		m_functions[packet_type] = f;
	}

	void packet_dispatch(int packet_type, packet_class* packet)
	{
		auto p = m_functions.find(packet_type);
		if (p != m_functions.end())
		{
			(p->second)(packet);
		}
	}
};

그럴 듯 보이는가? 사실 이 클래스를 사용하기에는 큰 문제점이 있다. class F가 클래스의 템플릿 파라미터라는 것이 문제다. 위 모양을 보면 템플릿 클래스가 컴파일러에 의해서 전개되기 전에 F 형이 반드시 결정되어 있어야 한다. 패킷 디스패치 기능을 만들어 내기 위해서는 다양한 함수를 등록할 수 있어야 하지만 이 템플릿은 클래스 하나당 함수 하나를 등록할 수 있기 때문에 목적에 맞지 않다. 방법이 없을까? 아니다. 이제 가장 중요한 템플릿 3개를 소개하겠다.

std::function (vs2008일 경우, std::tr1::function)
std::bind (vs2008일 경우, std::tr1::bind)
std::placeholders::_1, _2, ... (vs2008일 경우 std::tr1::placeholders::_1, _2,....)

function은 모든 호출 가능한 형을 대신할 수 있다.
bind는 모든 호출 가능한 형과 그 파라미터를 조합할 수 있다.
placeholders는 모든 알려지지 않은 호출 파라미터를 대표할 수 있다.

위에 소개한 템플릿의 유용성은 이루 말할 수 없을 정도이다. 꼭 사용법을 숙지해두길 권장한다.
위의 템플릿을 실제로 구현하기 위해서는 템플릿 리스트 기법을 사용하던가 아니면 c++11의 Variadic template을 사용해야 한다. 여기서는 템플릿의 사용 방법을 이야기 하고, 추후 간단히 구현 원리를 소개하겠다.



이 3가지 조합으로 모든 호출을 단일 펑크터로 만들 수 있다. 다시 dispatch_class를 수정해 보자.

class dispatch_class2
{
protected:
	std::map<int, std::function<void(packet_class*)>> m_functions;

public:
	dispatch_class2(){}
	void set_dispatch(int packet_type, std::function<void(packet_class*)> f)
	{
		m_functions[packet_type] = f;
	}

	void packet_dispatch(int packet_type, packet_class* packet)
	{
		std::function<void(packet_class*)> f = m_functions[packet_type];
		if (f)
		{
			f(packet);
		}
	}
};

이 클래스와 앞서 소개했던 함수 포인터 형태와의 차이점을 이해 하겠는가?
일견 비슷해 보이지만, 함수 포인터 형태는 그 형이 정해져 있다. 전역 함수이던, 멤버 함수이던 호출 파라미터를 비롯한 형에 관련된 정보를 개발자가 미리 맞춰 주어야 한다. function 사용 클래스는 유연하다. 다음 예제를 보자

class dispatch_test
{
public:
	void packet_1(packet_class* packet)
	{
	}
	void packet_2(packet_class* packet)
	{
	}
};

void d1(packet_class* packet)
{
}

void d2(dispatch_test* dt, packet_class* packet)
{
}

struct my_param {};

class dispatch_test2
{
public:
	void packet_1(my_param* p, packet_class* packet)
	{
	}
	void packet_2(packet_class* packet, int p2)
	{
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	dispatch_test instance;
	dispatch_test2 instance2;
	my_param mp;

	dispatch_class2 test2;
	test2.set_dispatch(0, std::bind(&dispatch_test2::packet_1, &instance2, &mp, std::placeholders::_1));
	// instance2의 멤버 함수를 등록하고, 추가로 mp의 포인터가 첫번째 파라미터로 전달되게 한다. 
	// dispatch_class에서 입력하는 packet_class* 는 두번째 파라미터로 전달한다.

	test2.set_dispatch(1, std::bind(&dispatch_test2::packet_2, &instance2, std::placeholders::_1, 100));
	//instance2의 멤버 함수를 등록하고, 추가로 두번째 파라미터로 int를 전달한다.
	
	test2.set_dispatch(3, std::bind(d1, std::placeholders::_1));
	//전역함수 d1을 등록한다.
	
	test2.set_dispatch(4, std::bind(d2, &instance, std::placeholders::_1));
	//전역 함수 d2를 등록하고 첫번째 파라미터로 instance를 사용하도록 한다.
	
	return 0;
}
dispatch_class와 비교하여 dispatch_class2는 함수 형에 거의 제한이 없다. bind 함수의 인자들은 bind 함수로 전달 되는 인자들은 bind로 만들어지는 클래스의 멤버로 어딘가에 저장되어 있다. 어딘가에 저장된 멤버들이 실제 함수로 넘겨질때, 비효율적으로 사용되지 않도록 주의해서 설계해야 할 것이다.








C/C++ , 좋은 프로그래밍 - 템플릿 - 2




Effective C++. 이 책은 C++을 공부하던 사람이라면 한번 쯤은 읽었을 고전 명작이다. STL의 원리와 철학에 대해서 자세히 설명하고 독자로 하여금 새로운 개발 철학을 익히게 해준다.
그러나 아래 이책은 아는 사람이 많지 않다.



Effective C++이 효율성에 대해서 눈 뜨게 해 주었다면, Modern C++ design은 몰라서 전전긍긍 했을 때의 내 답답함을 해소해 주었다. 그러나 이 책에서 설명하는 핵심적인 기술 몇 가지는 C++11에서 지원해주거나 더 확장해서 지원한다. 이제는 직접 구현할 필요는 없을 지라도 공부에는 큰 도움을 줄 것이다.

템플릿의 기본적인 문법은 단순하다. 아래 처럼 템플릿을 선언한 후 형이 지정되면, 그에 맞춰 템플릿이 자동으로 클래스로 변환된다.

template<class TYPE>
class type_parser
{
protected:
	TYPE m_instance;

public:
	type_parser(const std::string& src){}
	~type_parser(){}
	TYPE& get(){ return m_instance; }
	size_t size() const { return sizeof(m_instance); }
	const type_info& type() const { return typeid(TYPE); }
};
위의 예는 문자열 src에서 알 수 없는 형 TYPE의 값을 추출하기 위한 템플릿 클래스다. 개념적으로 TYPE은 어떠한 변수형 이라도 상관 없다. 그러나 문자열에서 특정한 형으로의 변환이 모두 같을 수는 없다. 정수형과 부동 소수점 형만 보더라도 atoi, atof 처럼 호출해야 하는 API가 달라진다. 우선 기본 변수형만 고려하여 보자.

template<>
class type_parser<int>
{
protected:
	int m_instance;

public:
	type_parser(const std::string& src)
	{
		m_instance = ::atoi(src.c_str());
	}
	~type_parser(){}
	int& get(){ return m_instance; }
	size_t size() const { return sizeof(m_instance); }
	const type_info& type() const { return typeid(int); }
};

template<>
class type_parser<double>
{
protected:
	double m_instance;

public:
	type_parser(const std::string& src)
	{
		m_instance = ::atof(src.c_str());
	}
	~type_parser(){}
	double& get(){ return m_instance; }
	size_t size() const { return sizeof(m_instance); }
	const type_info& type() const { return typeid(double); }
};
....

이런 식으로 원하는 템플릿 파라미터 형태에 관해서는 명시적으로 정의하는 것을 '템플릿 특수화(Template specialization)'라 한다.

type_parser<int> variable("1");
type_parser<double> variable2("2");
처럼 선언하면 템플릿 파라미터에 따라 미리 정의된 템플릿이 사용될 것이다.

하지만 무엇인가 답답하다. 기본형이 int, short, long, unsigned int 등 모든 경우에 위와 같이 정의해야 한다면, 비효율적으로 보인다.

반복적으로 쓰이는 클래스를 통합해서 하나로 줄일 수 없을까?

template<class TYPE
	, bool is_float
	, bool is_integer
>
class type_parser
{
protected:
	TYPE m_instance;

public:
	type_parser(const std::string& src){}
	~type_parser(){}
	TYPE& get(){ return m_instance; }
	size_t size() const { return sizeof(m_instance); }
	const type_info& type() const { return typeid(TYPE); }
};

이제 클래스를 위와 같이 확장할 것이다. 그리고 특수화를 아래와 같이 변경한다.

template<class TYPE>
class type_parser<TYPE, false, true>
{
protected:
	template<class T, bool is_64bit= sizeof(T)==8> struct inner_convert
	{
		static T convert(const char* t);
	};
	template<class T> struct inner_convert<T, true>
	{
		static T convert(const char* t) {return (T)::atoi(t);}
	};
	template<class T> struct inner_convert<T, false>
	{
		static T convert(const char* t) {return (T)::_atoi64(t);}
	};

protected:
	TYPE m_instance;

public:
	type_parser(const std::string& src)
	{
		m_instance = inner_convert<TYPE>::convert(src.c_str());
	}
	~type_parser(){}
	int& get(){return m_instance;}
	size_t size() const {return sizeof(m_instance);} 
	const type_info& type() const {return typeid(int);}
};

template<class TYPE>
class type_parser<TYPE, true, false>
{
protected:
	TYPE m_instance;

public:
	type_parser(const std::string& src)
	{
		m_instance = (TYPE)::atof(src.c_str());
 }
	~type_parser(){}
	double& get(){ return m_instance; }
	size_t size() const { return sizeof(m_instance); }
	const type_info& type() const { return typeid(double); }
};

특수화를 2개로 줄였고, 각 특수화에  TYPE형은 아직도 결정안된 것으로 남겨 두었다.
이제 아래에서 위의 두가지 경우는 동일한 template이 사용될 것이다.

type_parser<int, false, true> variable("1");
type_parser<long, false, true> variable2("2");
type_parser<double, true, false> variable3("3")

이런 문법을 '부분 특수화(Partial specialization)'이라 한다.
그런데 Integer 형 특수화의 경우 내부에 템플릿을 하나 더 정의하였다. TYPE형이 64bit인지 아닌지 구분해서 사용하기 위한 템플릿이다. 문자열에서 정수로의 변환이 64bit일 경우 다르기 때문이다. 부분 특수화를 사용했지만 템플릿 파라미터에 기본 파라미터를 미리 정의하였다.  inner_convert를 사용할때, is_64bit 값은 신경쓰지 않아도 자동으로 계산하게 되어 있다.
그러면 type_parser의 2,3 번 파라미터도 동일하게 적용할 수 있지 않을까? 가능하다. 단, 몇가지 선 작업이 필요하다.
TYPE 형이 부동 소수점인지 정수형인지 미리 판단이 가능해야 한다. 어떻게 할까? 간단하다.

template<class TYPE> struct is_float{ enum {value = 0}; };
template<> struct is_float<float>{ enum{value=1};};
template<> struct is_float<double>{ enum{value=1};};

template<class TYPE> struct is_integer{ enum {value = 0}; };
template<> struct is_integer<int>{ enum{value=1};};
template<> struct is_integer<long>{ enum{value=1};};
...
위와 같은 템플릿들을 미리 정의하면 된다. 템플릿을 특수화하여 원하는 경우에는 value 값이 1이 되도록 하면 된다. 위의 템플릿들은 std::is_floating_point, std::is_integral 이라는 이름으로 이미 선언되어 있다.
이제 템플릿 선언을 수정하면 다음과 같다.

template<class TYPE
	, bool is_float = std::is_floating_point<TYPE>::value
	, bool is_integer = std::is_integral<TYPE>::value
>
class type_parser;


template<class TYPE>
class type_parser<TYPE, false, true>
{
protected:
	template<class T, bool is_64bit= sizeof(T)==8> struct inner_convert
	{
		static T convert(const char* t);
	};
	template<class T> struct inner_convert<T, true>
	{
		static T convert(const char* t) {return (T)::atoi(t);}
	};
	template<class T> struct inner_convert<T, false>
	{
		static T convert(const char* t) {return (T)::_atoi64(t);}
	};

protected:
	TYPE m_instance;

public:
	type_parser(const std::string& src)
	{
		m_instance = inner_convert<TYPE>::convert(src.c_str());
	}
	~type_parser(){}
	int& get(){return m_instance;}
	size_t size() const {return sizeof(m_instance);} 
	const type_info& type() const {return typeid(int);}
};

template<class TYPE>
class type_parser<TYPE, true, false>
{
protected:
	TYPE m_instance;

public:
	type_parser(const std::string& src)
	{
		m_instance = (TYPE)::atof(src.c_str());
	}
	~type_parser(){}
	double& get(){return m_instance;}
	size_t size() const {return sizeof(m_instance);} 
	const type_info& type() const {return typeid(double);}
};
type_parser<int> variable("1");
type_parser<long> variable2("2");
type_parser<double> variable3("3");

템플릿의 사용은 훨씬 간편해 졌다. 그리고 특수화를 제외한 템플릿 선언을 제거하였다. 이렇게 되면 특수화에 해당하는 타입이 아니면 컴파일 에러가 발생한다. 특수화에 해당하는 int, long 등만 사용한다면 에러가 발생하지 않을 것이다.

위에 정의한 type_parser라는 템플릿 클래스를 함수로 만드는 것은 불가능할까?

template<class TYPE>
inline TYPE type_parse(const std::string& src)
{
}

위 처럼 사용해도 괜찮다. 단, 함수 템플릿은 부분 특수화를 허용하지 않는다. 그렇기에 위에서 사용하는 bool 템플릿 파라미터를 응용한 기법을 사용할 수 없다. 함수로 템플릿을 정의하려면, 사용하고자 하는 모든 TYPE에 대하여 특수화를 선언하면 된다.

함수 템플릿은 클래스 템플릿과 비교하여 한가지 장점이 있다. 함수 템플릿의 템플릿 파라미터는, 입력 받는 변수에 따라 TYPE을 자동으로 선택된다. 그러나 이런 특징은 템플릿 뿐만 아니라 함수 오버로딩에도 적용되는 규칙이다.  오버로딩과 함수 부분 특수화는 같이 사용될 경우 실제 호출 되어야 할 함수를 결정할 수 없다.
(사실, 이 설명은 정확하지 않다. 이문제를 이해하려면 '함수형 프로그래밍-Functional programming'를 알아야 한다. 그리고 나도 FP는 잘 모른다.... )






C/C++ , 좋은 프로그래밍 - 템플릿 - 1

클래스나 함수의 재사용률을 올리는 좋은 방법이 있을까? 뭐, 개발자마다 수 많은 의견이 나올 것이다. 그리고 그 수 많은 의견에서 템플릿 반드시 공통으로 언급될 것이다.

템플릿은 그 목적 자체가 재사용을 위해서 태어난 문법이나 다름 없다. 더군다나 템플릿 자체의 문법은 꽤 간단하다. 하지만 꽤 간단한 몇 가지 기능의 조합으로 아주 큰 효과를 발휘한다. 더군다나 C++11에 이르러 Variadic template 이라는 문법이 추가 되었다.

이제 기본적인 템플릿의 사용법부터 시작하여 본격적인 활용까지 이야기 해보고자 한다.

우선 템플릿 이야기를 하려면 먼저 알아야할 몇가지 문법을 언급하겠다.
가장 기본적으로 펑크터를 알아야 한다. 많은 사람이 알고 있겠지만 간단하게 이야기 해보고 시작하자.

class CFunctor
{
void operator()(int param){}
};

CFunctor functor;
functor(3);

기본적인 문법 구조는 위와 같다. 멤버 함수로 operator()() 함수를 정의하면 객체의 인스턴스를 함수처럼 호출 할 수 있다.

두번째로 함수 오버 로딩에 의한 자동 호출이다.

void func(int v){}
void func(double d){}

func(2)
func(2.0)

위의 예시처럼 func(2)와 func(2.0)은 각기 다른 함수가 호출된다. 이런 구조는 일반 함수 뿐만 아니라, 멤버 함수에도 적용된다.

이상으로 몇가지 기본적인 이야기는 마무리하고 본격적인 템플릿 이야기를 하자.


C/C++ , 좋은 프로그래밍 - 원칙

먼저 "어떤 프로그램이 나쁠까?"를 이야기 하자.

여러 문제를 댈 수 있겠지만, 나는 가장 중요한 것으로 에러 처리를 하지 않은 프로그램을 꼽는다.
프로그램이 개발되는 환경은 최적의 환경이다.
최신 라이브러리, 최신 PC, 검증된 네트워크 환경, 깔끔한 관리 등 최고의 환경에서 프로그램을 개발한다. 그러나 그 프로그램이 실행되는 곳은 최악인 경우가 종종 있다.
개발자의 개발 PC에서 나오지 않는 현상이 실제 유저의 손에서 동작하면 많은 버그를 뱉어낸다. QA를 통해서 일정 부분 걸러지기도 하지만 QA를 통과하는 버그는 반드시 존재한다.

에러 처리가 없다면 이런 버그는 심각한 문제를 유발할 때가 종종 있다. 에러 처리가 없는 그 부분에서 버그가 발생하는 것은 행복한 경우다. 그 버그가 돌고 돌아 엉뚱한 곳에서 문제를 일으킨다면 개발자의 PC에서 버그를 찾을 방법은 사실상 없다고 봐도 된다. 그 사용자의 PC와 똑같은 환경을 세팅하던지, 덤프 파일을 받아서 추적하지 않으면 버그를 찾는 것은 거의 불가능하다.


아무리 친숙한 API라도 API에 리턴 값이 존재하고 잘못 동작하는 경우가 명시되어 있다면 반드시 그 값을 검사해야 한다. 팀으로 작업을 하고 있다면 옆의 작업자를 믿으면 안된다. 그 작업자가 넘기는 파라미터와 결과 값은 반드시 검증하는 부분이 있어야 한다.  심지어 이런 원칙은 자기자신에게도 적용해야 한다. 스스로를 믿지 말고 값을 검증해야 한다.
추가로 반론의 여지가 있겠지만, assert의 사용은 자제하라고 하고 싶다. 대부분의 assert는 디버그 모드에서 동작한다. 디버그로 프로그램을 배포할 것이 아닌 이상, assert는 정말 확실한 부분에 최소로 사용하고, assert를 넣을 부분에 에러 처리 루틴을 스스로 만들어 사용하라.

에러 확인이라는 원칙과 더불어 중요한 것은 전체 프로그램의 에러 처리의 과정이다. 개인이면 개인적인 에러처리 과정이 있어야 하고 팀이면 팀 전체에서 유일하게 사용되는 에러 처리 과정이 있어야 한다. 팀 작업에서 에러 처리 과정에 개인 취향을 두어서는 안된다.
하나의 에러 처리 과정은 동료와 과거의 자신을 신뢰하게 하는 수단이다.

개인적으로, 과거에는 모든 클래스에 get_last_error 라는 함수를 두고, 모든 함수는 결과의 성공/실패를 리턴하게 만들었었다. 그러나 몇년간, 프로그램을 만들때마다 느낀 것은 '귀찮다'였다.  그래서 요즘은 try-catch를 사용한다. 단, 모든 예외는 STL의 std::exception만 사용하고 예외를 발생시킬 때는 반드시 이미 만들어 둔 매크로만 사용한다.
귀찮음 때문에 바꾸었지만 상당히 만족스럽다. 만들어 둔 매크로에는 에러 내용, 에러 번호, 발생 함수, 발생 줄 등의 모든 정보를 기록하기 때문에 한눈에 버그가 발생한 부분을 알 수 있다.
개인이든, 팀이든 에러 처리 과정을 반드시 정해 두길 바란다.

이 원칙을 지키고 있다면 어느 순간 자신이 만든 프로그램에 자신이 붙을 것이다. 에러가 발생해서 경고창이 떴다면 그에 맞는 대처를 설명해주면 된다. 또는 친절하게 에러를 설명했다면 사용자는 버그 내용은 레포트 하지도 않고 스스로 해결해서 사용할 것이다.






C/C++ , 좋은 프로그래밍 - 들어가며

좋은 프로그래밍에 대한 기준은 너무나 많다. 그러나 주관적인 관점에서 몇 가지 핵심적인 것만 축약한다면 몇가지로 나타낼 수 있다.

1. 협업을 쉽게 할 수 있는 구조이어야 한다.
2. 버그 추적을 쉽게할 수 있고 구조적으로 버그의 가능성을 줄여야 한다.
3. 유지 보수가 쉬워야 한다.
4. 재사용 가능한 코드를 모아서 관리해야 한다.

4가지 정도가 적당해 보인다.
사실, 기초적인 프로그래밍 지식과 API에 대한 숙지가 되어 있다면 결과물은 어떻게든 나오게 되어 있다.  C++의 개체지향 구조는 C로 바꾸어 만들 수 있고, C의 결과는 어셈블러로 바꾸어 표현할 수 있다. 왜 고급 언어를 쓰고 더 복잡한 프로그래밍 문법을 쓸까? 답은 단순하다 더 좋은 프로그램 결과를 더 짧은 시간에 더 효율적으로 만들기 위해서다.

지금부터는 지금까지 경험했던 여러가지 코드들 중 몇개를 골라서 소개할 것이다. 물론 주관적이기 때문에 다 동의하지 못할 수도 있다. 그러나 분명히 좋은 참고는 되리라 본다.

가장 먼저 템플릿을 설명할 것이다. 템플릿은 개발자라면 누구나 알고 있는 문법이지만 실제로 잘 활용하지 못하는 것 같다. 더구나 이미 개발된 좋은 템플릿 코드들도 쓰지 않는 개발자가 많다. 몇가지 예를 들며서 그 활용방안을 이야기 하자.
그리고 모듈화에 대하여 설명할 것이다. 모듈화라면 모두들 이미 알고 있겠지만 실제로 실천하는 개발자 역시 드물다. 여기서는 모듈화를 실제로 활용하면 왜 좋은지 설명하면서 몇년간 개발 하면서 만든 모듈화 관련 코드를 소개하겠다.
다음으로 Managed C++에 대해 소개하겠다. MS가 .NET을 소개하면서 만든, .NET용 C++이다. 최근의 Windows phone 및 windows 8이후의 APP 프로그램은 .NET C# 혹은 Visual basic, Managed C++을 이용해야 한다.

마지막으로 위의 것들을 종합해서 만든 개인 라이브러리 중 몇가지를 소개하겠다.

C++ 기준 설명이다.

2015년 6월 7일 일요일


어렸을 때, 세계사를 배우다 보면 드는 의문이 있다. 주요 발명품은 동양에서 나왔는데, 근대에 이르러 왜 유럽이 세계를 제패했을까? 대륙에 살고 있는 문명간의 불균형한 발전은 왜 그럴까?

"총, 균, 쇠"는 이런 의문에 대한 명쾌한 해답을 제시한다. 논리적 정합성 뿐 아니라, 근거를 제시한다.
호모 사피엔스가 출현한 이후, 인류가 생물학적 유전적으로 진화했다는 증거는 현재로선 없다.  인류는 1만년 전, 농경 사회에 진입하기 전에는 모든 대륙에서 비슷한 문명 수준을 향유했다. 그러나 농경 사회에 진입한 이후는 다르다. 최소 50만년간 정체되어 있던 문명은 이후 1만년 동안 정말 가파르게 발전한다.
이런 발전의 뒤에는 '환경'이라는 운이 존재했다. 농장에 적합한 식물과 가축화에 적합한 동물이 폭발적인 인구 증가를 뒷받침 하였다. 그러나 적당한 식물이나 동물이 없다면 즉 '운'이 없다면 그렇지 못했다. 여기에서 인류 사회의 차이가 벌어지기 시작했다.

가축화 된 동물은 그 사회에 커다란 이점으로 다가왔지만, 때로는 지금 껏 경험하지 못했던 바이러스와 세균를 인류에게 전파하기도 했다. 직접 부딪혔던 사회는 어떻게든 극복했지만, 그것은 큰 문제를 내포한 미래의 재앙이었다.
문명이 발전하니, 사회간의 접촉이 발생했고 세균이나 바이러스를 처음 겪는 사회는 그대로 멸망하며 내며 역사의 뒤안길로 사라졌다.

중세까지는 중국이 인류의 발전을 선도했다. 그러나 중국은 통일 왕조가 나타날 때마 왕조에 위협이 될 수 있는 발전된 문물을 파괴했다. 그들의 입장에서 가장 좋은 것은 변화가 아니라 정체였다. 반명 유럽은 지리적 문제로 통일된 적이 없었고 항상 경쟁했다. 그들에게 변화가 없다는 것은 뒤쳐짐을 뜻했다.

이런 환경적인 문제는 결국 사회간의 발전의 격차를 만들어냈다. 인류는 이제는 환경 문제를 어느정도 극복했다고 하지만 아직도 환경은 우리에게 큰 영향을 미친다.
인류가 지구를 벗어나 우주로 나갈 수준이 아닌 이상 환경에 종속적일 수 밖에 없다. 앞으로 미래에서 환경이 가지는 의미를 다시금 되새겨 봐야하지 않을 까?

2015년 6월 4일 목요일


꼭 해보고 싶은 목표가 있다. 소설 쓰기다. 몇번 시도를 해보지만 한 두줄 쓰고 고치기 일수다. 진도는 너무 더디다.
나름 책은 많이 읽는다고 자부한다. 머리 속에는 줄거리도 이미 만들었고 전달하고 싶은 메세지도 정해놨다. 그런데 거기서 끝이다. 정말 글쓰기는 어렵다는 것을 체감했다.

"유시민의 글쓰기 특강"은 소설 쓰는 방법에 대한 이야기는 아니다. 소설은 작가도 어렵다고 토로한다. 다만 어떤 글이 좋은 글이고 그런 글을 쓰기 위해서는 무엇을 해야 하는지 설명한다. 생생한 경험과 눈에 확 들어오는 예시는 덤이다.

"단문을 사용해라!", "책을 많이 읽어라!", "쓰기 훈련을 하라!"

작가가 반복적으로 강조하는 것이다.  사실, 이렇게 블로그에 내가 읽었던 책들에 대한 감상평을 적기 시작한 것도 이 책을 읽고 나서다. 작가가 강조하는 훈련을 하기 위해서다. 책에서는 손으로 직접 쓰는 것을 추천했지만, 나름 IT에서 일하는 입장에서 블로그 하나 없는게 아쉬웠기에 일단은 블로그부터 시작하고자 했다.
더불어 몇 가지 책을 추천 했는데 이미 보고 소장하고 있는 책이 나와서 반갑기도 했다. 그리고 "토지"는 반복적으로 추천하고 있다. 작가의 글을 보면 "토지"만 몇 번 보면 갑자기 글쓰기 능력이 껑충 오를 것 같다. 아직 읽어보지 않았기에 바로 구매할 예정이다. 작가의 말 처럼 다섯 번 읽기는 무리겠지만 두어번은 읽어 보고 다시 소설 쓰기에 도전할 생각이다.

꼭 글을 쓰기 위한 목적으로만 이 책을 볼 필요는 없다. 읽는 능력을 올리는데도 효과적인 것 같다. 나름 책을 많이 읽어 독서 능력이 좋은 줄 알았지만, "유시민의 글쓰기 특강"을 보고 나니, 그것도 자만심이었다. 작가가 전달하고자 하는 내용뿐 아니라, 그 글의 내실을 이해하는 것도 중요하다. 책의 내용을 그냥 받아들이는 것보다 비판할 수 있는 능력도 중요한 것이다. 그래야 그 내용을 온전히 체득한다고 볼 수 있다.

지금은 글 쓰기에 대한 공포가 약간은 줄어 들었다. 이래서 평생 공부한다고 하는가 보다.


2015년 6월 2일 화요일



경제는 어렵다. 그들만의 언어를 사용하고, 원인과 결과는 축약되어서 파악할 수 없다.
몇 권의 경제학 책을 들여다 보았었다. 기초 경제학책은, 요즘 신문/TV에서 말하는 용어를 설명해 주지 않았다. 요즘의 경제학 책은 기초적인 원리는 다 생략되어 있었다.

난 경제학이 무척 어려운 것으로 알고 이었다. 기초적인 원리를 공부한 후 현대의 경제 이론을 또 공부해야 하니 일반인으로서 그냥 포기하고 말았었다.

[장하준의 경제학 강의]는 이런 나의 생각을 멋지게 깨뜨렸다. 경제 용어 어렵지 않았다. 파생 상품, 은행, 펀드 어렵지 않았다. 물론 이 책을 읽는 다고 전문가가 되는 것은 아니다. 그저 TV에서 말하는 내용의 문맥을 대충 이해하고 궁금하면 찾아볼 정도가 되었다. 까막눈에서 그렇게 발전한 것만 해도 어딘가!

더욱이 [장하준의 경제학 강의]의 가치는 현대 경제에 드리워진 어두운 점을 알고 이해하게 해준다. 파생 상품의 원리와 그 파급효과, 새롭게 발전된 금융 시스템의 문제점, '주주 가치 극대화'란 목표의 위험성, 우리 나라의 수출 지향적인 경제 정책과 다른 선진국과의 비교, 전 세계 다국적 기업과 대기업 들이 금융에 왜 그렇게 목을 매는지 알게 해주었다.
한쪽으로 치우쳐진 견해일지는 모른다. 그러나 일반인에게 문제 의식은 느끼게 해주고 경계할 수 있다는 점 만으로도 이 책의 가치는 충분하다.

경제학은 학문이 아니다. 생활이고 사회 그 자체다. 인간은 집단을 이루어 발전의 역사를 만들어 냈지만, 인간의 기본적인 욕망과 불합리함은 결국은 발전을 가로 막고 있다. 모두가 가난한 것만이 투쟁과 반목의 역사를 없앨 수 있을까? 인간이 불완전한 이상 유토피아는 허구일까?


2015년 6월 1일 월요일


진화의 근본 원리는 무엇인가?

진화는 유전자를 위해 존재하는가, 유전자가 속한 개체를 위해 존재하는가, 혹은 개체가 속한 전체 종을 위해 존재하는가?

이기적 유전자는 그 답으로 그 어떤 집합도 아닌 gene이라 불리는 유전 단위 자신을 위해 "진화"라는 과정을 만들어 낸다고 한다.

gene은 정보다. 정보는 물리적 실체가 없다. 그러므로 사실 gene도 실체는 없다. 다만 gene은 그 정보의 현신을 위해 단백질을 이용하는 것이다. 그리고 그 현신이 지속할 수 있도록 자신과 비슷한 다른 gene과 협업하여 DNA를 만들어 내고, 주어진 환경에서 그 정보가 퍼지도록 생명체라는 개체를 이용하고 종, 속, 문 등의 개체 집단을 이용하는 것이다.

생명체의 삶과 죽음, 종의 멸종은 어떤 원리일까?
외부의 파괴적인 환경에서 스스로를 보호하기 위해서는 협업이 필수다. 단일 유전자는 외부 환경에 능동적 대응을 할 수 없다. 능동적 대응을 위해 협업을 하지만 그 협업은 다른 문제를 낳는다.
만일 동업자가 불량이라면? 동업자가 배신을 한다면? 특정 환경에서는 꽤 괜찮았던 동업자가 다른 환경이 닥치자 정신을 못차리고 민폐만 끼친다면? gene은 자신을 제외한 어떤 것도 믿지 못한다. 더군다나 유전 정보를 담고 있는 DNA는 그리 안정적인 물체가 아니다, 고에너지에 의해 쉽사리 변질 될 수 있다.
이런 관점에서 유전자의 교환과 생명체의 죽음은 조금 괜찮은 해법이라 할 수 있다. 교환을 통해 지속적으로 현재의 환경에서 최적의 동업자를 찾고, 죽음과 탄생을 통해서 혹시 모를 개체의 오류를 리셋할 수 있다.

정보라는 gene의 특성을 확장하면 더 meme이라는 개념까지 도달할 수 있다.
현상을 바라보는 관념, 관습, 예술 등은 하나의 정보다. 정보의 전달 매체가 DNA, 생명체, 종이라는 관점을 벗겨내, 집단 자체 즉 사회라면 어떨까? 사회의 구성원 하나 하나가 meme을 구성하는 단백질이 되고, 그것이 모여 사회라는 DNA 및 개체가 된다면 꽤 그럴듯 하지 않은가?

추가로 이기적 유전자를 읽다 보면 지능에 대한 의문점에 도달한다. 우리가 말하는 지능, 본능, 이성은 무엇일까? 단순히 생물학적으로 지능은 뉴런간의 상호 작용이다. 그런데 그 상호 작용은 호르몬의 절대적 영향을 받는다. 그리고 그 호르몬은 유전자의 지배를 받는다. 그러면 지능은 gene을 위한 장치일 뿐 그 이상의 의미는 없는 것이 아닐까?
gene의 시간 흐름은 백만년 이상 1억년 까지 간다. 그러나 환경의 시간 흐름은 소위 실시간 단위의 흐름이다. 반사 작용, 지능은 이런 시간의 괴리를 해결 할 수 있는 수단이 아닐까?

그러면 자아는 무엇일까? 지능을 인공적으로 만드는 것이 과연 가능할까? 다음에 한번 고찰해 봐야겠다.