Category: Programming language

arraysizeof macro

Posted by – 2009-10-12

다음 macro는 array의 length를 구하는 것인데 해석이 되시나요?

template <typename T, size_t N>
char (&Helper(T (&array)[N]))[N];
#define arraysize(array) (sizeof(Helper(array)))

...

안되신 분들을 위한 설명입니다. ;)

위 코드는 다음 두가지 사실만 알고 있다면 (비교적) 쉽게 이해됩니다.

sizeof는 function call syntax에 대해서도 동작하며 그 function의 return type에 대해 동작한다.

int foo() { return int(); }
...
sizeof(foo()) == sizeof(int)

array type의 reference를 리턴하는 method의 signature는 다음과 같다.

char (&bar)[10]() { ... }

이를 바탕으로 arraysizeof를 만들려면 char(&)[N]을 리턴하는 method를 만들면 되죠. 여기서 N은 array의 length.

template <typename T, size_t N>
char (&Helper(T (&array)[N]))[N] {
...
}

위 함수는 T(&)[N]을 parameter로 받아 char(&)[N]을 리턴합니다. 물론 T(&)[N] parameter는 이 template method의 implicit instantiation이 동작하도록 하기 위해 필요합니다. 마지막으로 sizeof는 method body가 필요없으므로...

template <typename T, size_t N>
char (&Helper(T (&array)[N]))[N];

끝.

참고로 이런 코드가 필요한 이유는 널리 쓰이는 "sizeof(array) / sizeof(*array)" macro가 다음과 같은 경우 compile error없이 오동작하기 때문입니다.

void foo(int array[]) {
  int size = sizeof(array);
}

이 코드에서 array는 pointer와 같이 취급되어 sizeof(array)는 sizeof(int*)가 됩니다.

Java generic in return context

Posted by – 2009-09-25

아래 코드를 보시고 1, 2번 라인중에 어디서 에러가 날 지를 찾아보세요.

interface B {
  void doB();
}
class D implements B {
  public void doB() {}
}
interface H {
  B getB();
}
class HImpl implements H {
  public B getB() {
    return new D();  // 1)
  }
}
...
H h = new HImpl();
D d = h.getB(); // 2)

네, 2번 라인입니다.

그럼 다음 코드는?

interface B1 {
  void doB1();
}
interface B2 {
  void doB2();
}
class D implements B1, B2 {
  public void doB1() {}
  public void doB2() {}
}
interface H {
  <T extends B1 & B2> T getB();
}
class HImpl implements H {
  public <T extends B1 & B2> T getB() {
    return new D();  // 1)
  }
}
...
H h = new HImpl();
D d = h.getB(); // 2)

네, 1번입니다. 잘 이해가 되질 않아 사내 메일링 리스트에 물어보니

I think you're seeing and interpreting it as "this method can return anything that implements both interfaces". What it's actually saying is "the caller is going to tell you a specific class that implements both interfaces, and you must return one of those".

이랍니다. 즉 T 타입이 아직 결정되지 않은 상태라 D와 T는 compatible한 타입이 아닌거죠.

강제로 casting을 해서 컴파일이 되게 만들면 그 코드는 runtime error (ClassCastException)를 발생하게 됩니다. 다음 코드를 보세요. 즉 이런 방법으론 도저히 type-safe한 코드를 작성할 수 없습니다.

class D2 implements B1, B2 {
  public void doB1() {}
  public void doB2() {}
}
...
D2 d = h.getB(); // ClassCastException!!

이 같은 상황을 방지하기 위해 1번 라인에서 에러를 내 주는 것이죠.

코딩 가이드라인을 만들어 보자면 "method의 parameter로 사용되지 않는 type variable을 사용하면 안된다"입니다. 다음과 같은 경우는 parameter로도 사용되고 있으므로 문제가  없는 경우들입니다.

<T> T makeInstance(Class<T> cls) { return cls.newInstance(); }
...
<T extends Comparable<? super T>> T min(T a, T b) {
 return a.compareTo(b) < 1 ? a : b;
}

위의 예제 코드같은 경우 아래와 같은 방법으로 type-safe하게 작성될 수 있습니다.

interface B1 {
  void doB1();
}
interface B2 {
  void doB2();
}
class D implements B1, B2 {
  public void doB1() {}
  public void doB2() {}
}
interface H  <T extends B1, B2> {
  T getB();
}
class HImpl implements H<D> {
  public D getB() {
    return new D();  // 1)
  }
}
...
H<D> h = new HImpl();
D d = h.getB(); // 2)

사실... 아직도 잘 모르겠어요. Java generic. =(

Access level of methods in package-private class

Posted by – 2009-09-22

Java에서 (C++도 마찬가지지만) 권장되는 access level 사용법은 다음과 같습니다.

Use the most restrictive access level that makes sense for a particular member.

쉽게 얘기해서 private으로 충분하면 private을 쓰고 다음으로 package-private, protected, public 순으로 쓰라는 얘기.

그런데 C++와는 달리 Java는 class에도 public과 package-private 두 종류의 access level이 있습니다. 이 중 public class의 method들은 위의 권장 사항을 따르면 될 듯 한데 package-private class일 경우는 어떨까요?

Package-private class에서는 실제 사용될 수 있는 조건으로 봤을 때 method들이 public이건 protected건 모두 package-private과 같은 조건을 가집니다. 즉, 아무리 method의 access level을 public이나 protected로 해봐야 다른 package에선 이 method를 사용할 수가 없습니다. 따라서 the most restrictive한 level을 사용하라는 위 권장 사항을 따르자면 package-private class의 method들은private이 아니라면 모두 package-private access level을 사용해야 합니다.

그런데 코딩을 하다보니 이 방법엔 한가지 문제가 있더군요. 가끔 package-private class를 public class로 만들어야 할 경우들이 생기는데 이때 method들이 모두 package-private이면 다른 package에서 사용할 수가 없습니다. 그래서 다시 method들의 access level을 조정해야 할 필요가 생기는데 이 시점은 이미 코드를 작성했을 때와 멀어서 자칫 잘못된 access level을 지정할 수 있게 됩니다.

그래서 생각한건데... package-private class의 method들을 만들 때 access level은 이 class의 access level을 public으로 가정하고 작성하는 것이 어떨까요?