예전에는 위의 두가지 Skill이 상당부분 유사하다고 생각했었다. 그런데 시간이 지나면서 위의 두가지 Skill에 작지않은 Gab이 존재함을 느낀다.
Debugging Skill(이후 D/S)과 Programming Skill(이후 P/S)간에 얼마만큼의 차이가 존재하는가?

한가지 예를 들어보자. 새로운 언어 (영어)를 배울때, reading skill과 writing skill간의 gap은 분명히 존재한다. 물론 극한에 이르려면 둘다 잘 해야 하는것은 명백하지만(만류귀종(?) 이라고 했던가..^^;), 어느정도 수준에 이르기까지는 서로 다른 방향에서 발전해 오다가 어느순간 교점을 가지는 듯 보인다. 과거 우리나라 학생들을 예로 들어보아도, reading skill은 뛰어나지만 writing skill은 부족한 경우가 많았다. 물론 그 반대도 존재한다. 대부분의 국내 대학생들의 reading skill은 미국의 저학년 초등학생의 그것보다 뛰어나다고 생각되지만, writing skill은 그렇지 못할 것이다. (어디까지는 필자의 생각이다.)

프로그래밍 언어도 마찬가지라고 생각한다. debugging을 위해서는 code reading skill(이후 R/S)이 중요하다. 그리고 Programming에는 code writing skill(이후 W/S)이 중요하다. 영어에서의 R/S, W/S간의 차이가 프로그래밍 언어에는 없으란 법이 있을까?
한 가지 예를 들어보면, P/S에서 가장 중요한 부분중에 하나인, design(class design, component design 등등) skill은 D/S에서는 별로 중요하지 않다. 반면, P/S의 경우, 내가 Programming할 분야 특화된 좁고 깊은 지식이 요구되지만,  D/S의 경우는 다양한 분야에 대한 넓고, (P/S에서의 경우대비 상대적으로)덜 깊은 지식이 필요한 경우가 많다.

각설하고, 요점은 이렇다.
두 Skill간에 차이는 분명히 존재하고, 이를 인정/감안 할 필요가 있다!
(물론, 만류귀종이라고... 어느 하나가 부족하다면, 다른 하나도 극한에 이를 수는 없다고 생각하지만....)

최근 나는, KLDP에서 data structure와 algorithm(이후 D/A)에 큰 관심이 없는 SW Engineer(이후 SWE)가 구글 코리아에 지원했다가 떨어진 내용에 대한 글 + 거기에 대한 글타래를 읽었다. (http://kldp.org/node/78668)
글타래를 보다보면, D/A에 익숙치 않은 SWE의 가치를 비하한다고 생각되어지는 몇몇 글이 눈에 띄인다. 난 이 post를 통해서 거기에 대한 반론을 전개하고자 한다.

모든 직종이 마찬가지겠지만, SW역시 다양한 분야가 존재한다. 그리고 Engineering skill의 가치는 "현재 종사하고 있는 분야에 얼마나 적합한가?"에 따라 측정되게 마련이다. 예를 들면, SW Integration Engineer의 경우, 각종 System 및 개발 환경, script와 tool의 사용 등에는 굉장히 뛰어난 skill을 보유하고 있을지 모르나, D/A에 대한 지식은 상당히 부족할 가능성이 높다. 그러나 그렇다고 해서, 뛰어난 SW Integration Engineer의 가치가 약해지거나 하는 것은 아니다. 그 사람은 본인의 직무에 적합한 skill을 가지고 있으므로, 우리는 그 사람을 충분히 '뛰어나다'고 이야기 할 수 있다. 나는 D/A가 중요하지 않다는 이야기를 하는 것이 아니다. 다만, 모든 SW분아가 D/A에 대한 skill을 요구하지는 않는다고 말하고 싶다.

SWE의 가치를 이야기할 때도 반드시 이러한 요소가 반영되어야 한다. 관련 글타래 중, "Google에서 정한 SWE의 가치(급여)가 현재의 급여보다 작다면, 현재 자신의 가치 이상의 급여를 받고 있을지도 모른다"는 의미의 글이 보인다 - 물론 내가 잘못 이해했을수도 있겠으나, 최소한 나에게는 이런 의미로 다가왔다. 그러나, 이는 분명히 잘못된 논리이다. 물론 현재 그 사람이 과한 급여를 받고 있을지도 모른다. 그렇지만, 최소한 그 기준이 Google의 급여가 되어서는 안되는 것이다. 왜냐하면, 현재 그 사람이 종사하는 분야가 요구하는 skill과 Google이 요구하는 skill이 엄연히 다르고, 각 회사는 자신들의 분야에 초점을 맞추어 SWE의 가치를 측정하기 때문이다. 다시 말하면, 그 사람이 현재 받는 급여는 현재 회사가 요구하는 skill을 기준으로 했을 때의 가치이고, Google이 제시한 급여는 Google이 필요로하는 skill을 기준으로 했을때의 가치가 되는 것이다.

"어떤 사람이 모든 측면에서 더 낫다."란  있을 수 없다. 이는 Engineering의 세계에서도 마찬가지라고 생각한다. 어느 한 분야에 국한했을때, "A가 B보다 낫다."라고 이랴기 할 수 있는 것이지, "A의 SW Engineering skill이 B의 것보다 낫다."라고 이야기 하기는 대단히 어렵다. 따라서, SWE의 일반적인 가치에 대한 측정은 대단히 신중해야 한다.

In this case order is very important. Because Semantic uses Global, GNU Global should be loaded before loading Semantic.
The problems is, Semantic doesn't shows any warning or error even if order is wrong.
So, your .emacs should be like follows,

    ...
    [loading Global]
    ...
    [loading Semantic and setting for using Global]
    ...

듣기로.. Nokia는.. 어떤 문제에 대해서... 해결 방법을.. 찾을 때...

* 사람들을 모아서 해결책을 찾는 방법론을 listing한다. - brain storming
* 투표를 통해서 상위 2~3개를 뽑아낸다.
* 같은 의견을 가진 사람들끼리 모여서 팀을 형성한다. (2~3개 팀이 생긴다.)
* 각 팀별로.. 해결책을 찾는다....

--> 이 방법.. 상당히 괜찮아 보인다. 사람들은 자신과 신념/비젼을 달리하는 방향에 대해서는 최선을 다하기 힘들다. (무의식적으로 나태해지게 된다.)
그런데 이 방법은 자신이 믿는 바가 옳음을 보이는 형식이므로, 사람들의 적극적인 참여를 기대할 수 있다.

일반적으로, 사람은 의사결정에 본인이 참여한 정도만큼의 열정/책임의식 을 가진다고 한다. 따라서, 사람들의 열정을 이끌어내기 위한 중요한 방법 중 하나가, 의사 결정에 충분히 참여하도록 하는 것이다!

Error message : Android requires .class compatibility set to 5.0. Please fix project properties.

Solution
- go to "project -> property -> Java Compiler ->"
- Set "Compiler compliance level" to 1.5. And push "Apply" button.

Now it is resolved.

Interesting thing : Once this issue is resolved by changing "Compiler compliance", even if it we restore this value back to 1.6, this error isn't reproduced. I don't have any idea about the reason.

'Domain > Android' 카테고리의 다른 글

[Android] NDK issues (found)  (0) 2011.02.15
[Android] Classify unexpected cases  (0) 2010.10.29
[Android-Eclair] Miscellaneous tips.  (0) 2010.04.09
[Android-Eclair] Using exact size of Views  (0) 2009.12.14
[Android-Ecliar][Tips] build Android..  (0) 2009.12.03

This is example for using function pointer in C. (basically, C++ case can be easily inferred from C's one)

Syntax related with function pointer type is quite different from other normal(?) one.
So, here is summary.

* type definition
typedef void (*<type name>) (int, void*);

* variable definition
void* (*<var name>) (int, void*) = NULL;
=> define function pointer variable that's type is "void*(*)(int, void*)" and initialize it into 'NULL'.

* type casting.
fpointer1 = (void*(*)(int, void*)) fpointer2;
=> cast function pointer 'fpointer2' into "void(*)(int,void*)" type.

* function that returns function pointer - float(*)(float, float).
=> float (*get_ptr(char c))(float, float); <- parameter is "char c", return type is "float(*)(float, float)"

* function - returns function pointer - pointer variable .
=> void(*(*var_name)(char c)) (void*); <- return function pointer type is "void(*)(void*)", parameter is "char c" and variable name is 'var_name'

* declare function pointer variable whose return type is function pointer.
=> int(* (*get_func)(int))(int, int); <- parameter of function is "int" And it returns function pointer "int(*)(int,int)"

* typecasting to function pointer whose return type is function pointer.
=> int(*(*)(int))(int, int); <- pointed function's return type is "int(*)(int,int)&quot;.

* array of function pointer.
=> static int(*array_name[])(float) = { ... }; <- "int(*)(float)" typed function pointer array.

* pointer of function pointer
=> void*(**<var name>)(int, void*); <- <var name> is pointer of function pointer. it's type is "void*(**)(int, void*)"

Let's narrow the subject down to the 'C' case.

In case of implementing reusable library, sometimes we want to add some hidden information at the allocated memory used in the library. To achieve this, library may support special paired-interface. For example

    void* mylib_malloc(unsigned int size);
    void mylib_free(void* p);

To simplify discussion, let's assume that we want add just hidden 'int' value.
We can easily think two options.
- add hidden value to the end of allocated memory. -- (*1)
- add to the start of it. -- (*2)

But we cannot use (*1), because, in general, we cannot know size of memory that is allocated. Therefore there is no general way to access hidden value by address.
So, (*2) is only way we can use. Actually, this is general way to implement standard 'malloc' function, too.

then, memory block structure will be like this.

            returned value
             of 'mylib_malloc'
                 |
                 v
    +------------+-------------------
    | hidden-int | user-allocated
    +------------+-------------------

We should keep in mind these.
- Size of hidden value depends on data-align-constraint. That is, memory address of user-allocated-space should obey data-align-constraint. For example, in case of 32bit ARM, 4 byte data align is required. So, size of hidden value should be multiple of 4 to get 4-byte-aligned-address for user space.
- 'mylib_alloc/mylib_free' should make a pair. (Standard-free-function should not be used - it raises error.)

--- Sub-shell / Current shell ---

(*1)
    echo -e "aaa\nbbb\nccc\nddd" > tmp
    value=0;
    while read line; do
        value=$(expr $value + 1)
        echo $value
    done < tmp
    rm -f tmp
    echo "at last: $value"

    result:
    1
    2
    3
    4
    at last: 4

(*2)
    value=0
    echo -e "aaa\nbbb\nccc\nddd" | while read line; do
        value=$(expr $value + 1)
        echo $value
    done
    echo "at last: $value"

    result:
    1
    2
    3
    4
    at last: 0

It is interesting, isn't it? The main reason is, (*1) is executed in current shell. But (*2) is executed in sub-shell. In more detail, there is only one process(current shell) during run at (*1). But at (*2), there are three processes; current shell, process for 'echo' and process for 'while loop'.

--- Shell function ---

(*1)
    function func_name() {
        ...
    }

(*2)
    func_name() {
        ...
    }

(*2) is more portable than (*1). Bash supports both (*1) and (*2). But, dash - /bin/sh -> dash in Ubuntu - doesn't support (*1).

--- Syntax ---
'()' grouping and '{}' grouping is delicately different in it's syntax.

(echo 1; echo 2; exit 0) # OK
{ echo a; echo b; exit 1 } # syntax error
{ echo a; echo b; exit 1; } # OK

 
--- Test ---
'!' has higher priority than '-a' and '-o'.

Assume that there are two files; 'a' and 'b'

[ -r a -a -r b ] : TRUE
[ ( -r a -a -r b ) ] : syntax error
[ ( -r a ) -a ( -r b ) ] : syntax error
[ ! -r a -o -r c ] : FALSE
[ ! -r a -o -r b ] : TRUE

 
--- Replacement ---
'~' isn't replaced in "". So,

MYHOME=~ # OK
MYHOME="~" # it doesn't work as expected

 
--- Symbolic link ---
Assume following file structure.

/data/userA/
/data/userB/
/home/a (a -> /data/userA/)
/home/b (b -> /data/userB/)

Let's see the following test.

cd /home/
cd a
pwd
    -> '/home/a' is shown.
ls ..
    -> 'userA' and 'userB' are shown. -- (*a)
cd ..
pwd
    -> '/home' is shown -- (*b)
ls
    -> 'a' and 'b' are shown. -- (*c)
cd /home/
cd a
cp a.txt ../
    -> a.txt is created at '/data/' -- (*d)

As you can see, the way handling symbolic link is different among commands. In above test, 'cd' and 'pwd' work differently from 'ls' and 'cp'. Some commands - like 'cd' and 'pwd' - handle directory path of working directory but some - like 'cp' and 'ls' - doesn't.
Therefore, try to use 'absolute path' to avoid the case like this, if symbolic link is included in the directory path.

* We can use 'View.draw(Canvas cavans)' to draw the view on selected canvas manually. But, as documented, the view must do a full layout. See following example.

--- It doesn't work ---
TextView tv = new TextView(this);
tv.setLayoutParams(new LayoutParams(400, 400));
tv.setText("Test");
tv.draw(canvas);

--- It works ---
TextView tv = new TextView(this);
tv.layout(0, 0, 400, 400);
tv.setText("Test");
tv.draw(canvas);

'setLayoutParams' is not doing layout, but just setting parameters for layout. So, we should manually do layout - ex, calling 'layout' function. And then we can draw the view.

* In general, view's coordinate domain is user content area - excluding status bar area. But, in case of Popup, it includes status bar. For example, base origin of left and top value of function 'popup.showAtLocation(parent, NO_GRAVITY, left, top)' is top-left of LCD screen - Not user content area. So, popup shown by 'popup.showAtLocation(parent, NO_GRAVITY, 0, 0)' is partially covered by status bar.

* 'android:layout_weight' is only valid in LinearyLayout - It is natural.

* When array is passed from Java to native code, there are two opions - copy or not. If array size is small, this doesn't matter. But, in case of large array, this can be important. In Android-Eclair, array is never copied. So, developer don't worry about passing large-size-array to native. You can check this from the macro code "dalvik/vm/jni.c: GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname)".

"Signal and Slot" concept in QT is very impressive. So, I mimicked it. Here is my mimicked version sample.
(I know... This is humble... But, it has been useful for me..)

typedef struct {
  void*   obj;
  void*   func;
}___yZw_yzW_FiT___;
...
typedef void (*___yZw_yzW_FuncT1___) (void*, void*);
...
#define SLOT1_TYPE  ___yZw_yzW_FuncT1___
...
#define DEF_SLOT1(cLASS, sLOT, tYPE1) \
void  sLOT (tYPE1); \
static void ___##sLOT##_yZw_s___( void* ___yZw_t___,  \
                                  void* ___yZw_d1___) {\
  ASSERT( ___yZw_t___ && ___yZw_d1___ ); \
  tYPE1 a1;\
  memcpy(&a1, ___yZw_d1___, sizeof(tYPE1)); \
  ((cLASS*)___yZw_t___)->sLOT(a1); \
}
...
#define DEF_SIGNAL1(cLASS, sIGNAL, tYPE1) \
CArr<___yZw_yzW_FiT___>    ___yZw_##sIGNAL##_a___;\
void sIGNAL (tYPE1 ___yZw_a1) const { \
  for(int i=0; i<___yZw_##sIGNAL##_a___.Size(); i++) {\
    (*((___yZw_yzW_FuncT1___)(___yZw_##sIGNAL##_a___[i].func)))(___yZw_##sIGNAL##_a___[i].obj, \
                                                                (void*)&___yZw_a1 ); \
  }\
}
...
// argument [sIG_CLASS] is reserved for future use.
#define CONNECT(sIG_CLASS, sIG_OBJ, sIGNAL, sLOT_CLASS, sLOT_OBJ, sLOT) \
do{\
  int __yZw_i__; \
  for(__yZw_i__=0; __yZw_i__<(sIG_OBJ)->___yZw_##sIGNAL##_a___.Size(); __yZw_i__++){ \
    if( ((sIG_OBJ)->___yZw_##sIGNAL##_a___[__yZw_i__].obj == (void*)(sLOT_OBJ)) && \
      ((sIG_OBJ)->___yZw_##sIGNAL##_a___[__yZw_i__].func == (void*)&sLOT_CLASS::___##sLOT##_yZw_s___) ){ break; } \
  } \
\
  if(__yZw_i__ >= (sIG_OBJ)->___yZw_##sIGNAL##_a___.Size() ) \
  { /* Not in the list */ \
    ___yZw_yzW_FiT___   _info; \
    _info.obj = (void*)(sLOT_OBJ); \
    _info.func = (void*)(&sLOT_CLASS::___##sLOT##_yZw_s___); \
    ((sIG_OBJ)->___yZw_##sIGNAL##_a___).Append(_info); \
  } \
  else {;/* do nothing - duplicated slot is not allowed */ } \
}while(0);

// argument [sIG_CLASS] is reserved for future use.
#define DISCONNECT(sIG_CLASS, sIG_OBJ, sIGNAL, sLOT_CLASS, sLOT_OBJ, sLOT) \
do{ \
  int __yZw_i__; \
  for(__yZw_i__=0; __yZw_i__<(sIG_OBJ)->___yZw_##sIGNAL##_a___.Size(); __yZw_i__++){ \
    if( ((sIG_OBJ)->___yZw_##sIGNAL##_a___[__yZw_i__].obj == (sLOT_OBJ)) && \
      ((sIG_OBJ)->___yZw_##sIGNAL##_a___[__yZw_i__].func == &sLOT_CLASS::___##sLOT##_yZw_s___) ){ break; } \
  } \
  if( __yZw_i__<(sIG_OBJ)->___yZw_##sIGNAL##_a___.Size() ) { ((sIG_OBJ)->___yZw_##sIGNAL##_a___).Remove(__yZw_i__); } \
}while(0);
...

Here is example code to use this.

// Class definition
class CObj1
{
public:
  CObj1(void){
    _a = 0; _b = 0;
  }

  // virtual DEF_SLOT overriding.
  void  Name(char*);

  // DEF_SLOT functions.
  DEF_SLOT1(CObj1, Set_A,
            int);
  DEF_SLOT1(CObj1, Set_B,
            int);

  // DEF_SIGNAL functions.
  DEF_SIGNAL1(CObj1, Put_A,
              int);
  DEF_SIGNAL1(CObj1, Put_B,
              int);

private:
  int   _a;
  int   _b;
};
...

// Main code

int main() {
  CObj1 o1_1;
  CObj1 o1_2;

  o1_1.Set_A(1);
  o1_1.Set_B(2);

  CONNECT(CObj1, &o1_1, Put_A,    // Signal
          CObj1, &o1_2, Set_B );  // Slot
  CONNECT(CObj1, &o1_1, Put_A,    // Signal
          CObj1, &o1_2, Set_A);   // Slot
  ...
}

+ Recent posts