Hatena::ブログ(Diary)

blanket log

2008-11-28

おばふろ

こういうコード書いたらコンパイラに警告出させたいとかよく思う.

int a, b;

...

long long c = a * b;

整数オーバーフローが起こりそうな箇所をおおざっぱに検出してくれるツールとかないんかな.

2008-10-29

コンパイルが止まらない

なんか珍しいものを見た.TopCoderではコンパイルに30秒以上かかっちゃダメらしい.

f:id:blanketsky:20081029122342p:image

コンパイルができないと何の情報も無いまま目だけでデバッグしなくちゃならないので精神的にとても辛い.

どうやら原因はでかいメンバ変数のようです.

#include<vector>
class C {
  std::vector<int> a[10000000];
};

上のコードをコンパイルするとずんずんメモリを喰っていきます.

実行時でなくコンパイル時に負荷がかかるというのは,おそらくクラスのサイズ決定時にテンプレートが絡んでほげほげだろうとは思うのですが詳しくは謎.

ともかく,こういうでかい変数は原則としてグローバルか static に取りましょうってことですね.もう嫌だこの言語.

2008-08-28

カリー化デコレータ

Python のデコレータはなかなか面白い機能だと思ったので、試しにカリー化を実装してみました。

@curry
def f(x,y,z):
    ...

などと書くと、カリー化されたfが定義されます。まぁ functools.partial があれば全然必要無いシロモノではあるのですが。

複数まとめて関数適用するとき f(x)(y)(z) とか気持ち悪いので、f.apply(x,y,z) みたいに普通っぽく呼び出せるようにもなっています。

可変長引数とは相性悪いです(*付き引数=引数1個ぶんとして扱われます)。


def arity_of(f):
    return len(f.func_code.co_varnames)

class curry:
    def __init__(self, f, n=None, args=None):
        self.func = f
        self.arity = n if n != None else arity_of(f)
        self.args = args   # linked list

    def __call__(self, x):
        f = curry(self.func, self.arity-1, (x, self.args))
        if f.arity <= 0:
            return f.__apply_args()
        else:
            return f

    def __apply_args(self):
        args = []
        l = self.args
        while l != None:
            args.append(l[0])
            l = l[1]
        return self.func(*reversed(args))

    def apply(self, *args):
        f = self
        for x in args:
            f = f(x)
        return f


if __name__=='__main__':
    @curry
    def test(x, y, z):
        print x, y, z
        return x + y * z

    print test(1)(2)(3)
    print test.apply(1,2,3)
    f = test(1)
    g = test.apply(1,2)
    print f.apply(2,3)
    print g(3)

2008-08-25

考えてみてくださいとかいいつつ

コレ

result = HogeX.invoke(x, "methodName", param1, param2);

自分でてきとうに書いてみました。 Class.getDeclaredMethod() が微妙に使えない子だった(シグネチャが引数に完全一致するメソッドしか取ってこれない)。

一応実行時に型チェックしつつ、protectedメソッドにもちゃんとアクセスできます。プリミティブ型の扱いがぞんざいなのはしょうがない。Javaだし。

多重継承か mixin が欲しくなる感じのアレです。

import java.lang.reflect.Method;

public class MethodSeek {
    public static Method getMethod(Object obj, String mname, Object... args)
        throws NoSuchMethodException {
        Class klass = obj.getClass();
        Method[] methods = klass.getDeclaredMethods();
        for (Method m : methods) {
            if (m.getName().equals(mname)) {
                if (typeCheck(m.getParameterTypes(), args)) {
                    return m;
                }
            }
        }
        throw new NoSuchMethodException(klass.getName() + "." + mname + "()");
    }
    protected static boolean typeCheck(Class[] types, Object[] args) {
        if (types.length != args.length) return false;
        for (int i = 0; i < types.length; i++) {
            if (!(types[i].isInstance(args[i]))) return false;
        }
        return true;
    }
}

こんなかんじで HogeX を定義できます。

import java.lang.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Hoge {
    private int a;
    public Hoge(int a) { this.a = a; }
    protected Integer prot(Integer b, Integer c) {
        System.out.println("Hoge:protected");
        return a + b + c;
    }
}

class HogeX extends Hoge {
    public HogeX(int a) { super(a); }
    public static <T> T invoke(Object obj, String mname, Object... args)
        throws IllegalAccessException,
               InvocationTargetException,
               NoSuchMethodException {
        Method m = MethodSeek.getMethod(obj, mname, args);
        return (T)(m.invoke(obj, args));
    }
}

class Test1 {
    public static void main(String[] args) {
        try {
            Hoge x = new Hoge(100);
            Integer result = HogeX.invoke(x, "prot", 30, 4);
            System.out.println(result);
            result = HogeX.invoke(x, "prot", 1, 2, 3);
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

実行結果

Hoge:protected
134
java.lang.NoSuchMethodException: Hoge.prot()
	at MethodSeek.getMethod(MethodSeek.java:14)
	at HogeX.invoke(test1.java:20)
	at Test1.main(test1.java:31)

2008-08-24

アクセス制御の抜け穴 (C++/Java ; protected編)

さて C++ と Java です。

privateへのアクセスはひとまず後回しにするとして、まずは protected なところから考えていきましょう。

C++ の場合 (protected)

欲しいメソッドがサブクラスからアクセス可能なので、自分でサブクラスを定義することで簡単に手に入ります。

Hoge クラスのオブジェクト x に対して prot() メソッドを呼び出したいというシチュを考えましょう。

class Hoge {
public:
  Hoge(): a(0) {}
  void p() { cout << a << endl; }
protected:
  void prot(int b) { a = b; }
private:
  int a;
};
int main() {
  Hoge x;
  x.prot(3);  // error
}

C++ではサブクラスで同名のメソッドを public に再定義可能です。(これができないオブジェクト指向言語ってあるのだろうか?)

アクセス指定のみ変更したサブクラスを作り、参照やポインタをダウンキャストしてやるとそのまま使えます。(これが安全でないこととかあるのだろうか?)

Hogeクラス自体が継承されることを想定されていない(メソッドにvirtualが指定されていない)場合はHogeXオブジェクトを作るのはまずいのでコンストラクタをprivateにしてあります。

class HogeX : public Hoge {
public:
  void prot(int b) { Hoge::prot(b); }
private:
  HogeX(){}
};
int main() {
  Hoge x;     x.p();
  HogeX &y (static_cast<HogeX &>(x)); // downcast
  y.prot(3);  x.p();
}

このようなサブクラスを定義してやれば、xを使う側は「protected メソッドが public になった x 」を得られるようになります。

ただし protected なメンバ変数へアクセスするには、getter/setter をサブクラスで別途用意してやる必要があります。

また、static な protected メソッドも、HogeX経由で public にしてしまうことができます。

Java の場合 (protected)

同一パッケージ内からは普通にアクセスできますので、別パッケージからのアクセスを考えます。

Java だと上のようなことはできません。自分の実行時の型より下へのダウンキャストは全て例外になるからです。次のコードはコンパイルは通りますが実行すると例外を吐いて死にます。

class Hoge {
  protected void prot() { System.out.println("Hoge:protected"); }
}
class HogeX extends Hoge {
  public void prot() { super.prot(); }
}
class Client {
  public static void main(String[] args) {
    Hoge x = new Hoge();
    HogeX y = (HogeX)x;   // ClassCastException
    y.prot();
  }
}

Javaはキャストに関しては相当保守的で、以下のような自明に安全なダウンキャストすら例外を吐いてしまいます。

class Hoge2 extends Hoge {}  // 何もしない!
class Client {
  public static void main(String[] args) {
    Hoge x = new Hoge();
    Hoge y = (Hoge)((Hoge2)x);   // 結局同じ型!
  }
}

どうやら「 x から"protected メソッドが public になった x "を得る」ことは難しそう*1なので諦めることにして、次に「xの任意の protected メソッドを実行する」ことを考えましょう。これぐらいならできます。

class HogeX extends Hoge {
  public static void callProt(Hoge x) { x.prot(); }
}
class Client {
  public static void main(String[] args) {
    Hoge x = new Hoge();
    HogeX.callProt(x);
  }
}

もうちょい一般的な形で

result = HogeX.invoke(x, "methodName", param1, param2);

のようなメソッドもリフレクションと可変長引数とジェネリクスを使って作れそうな気がします。(「可変長」と「オーバーロード対処」と「引数の型を明示的に与えなくてよい」を全部見たすのは難しいかも。Java詳しい方は考えてみてください。=>実装例)



―――privateへのアクセスについてはまた後で書きます。

関数ポインタ

全部わかったらえらいです。

#include <stdio.h>
void f(){}
int main() {
  void (*p)() = f;
  printf("%x, %d\n%x, %d\n%x, %d\n%x, %d\n%x, %d\n",
         &f, sizeof(&f),
         f , sizeof f,
         *f, sizeof(*f),
         p , sizeof p,
         *p, sizeof(*p));
  return 0;
}

実行結果: C C++

=f は型的には =&f なのですね。

*は何個くっつけても変わらんぽい。***f とか ******p とか。

サイズ 1 になってるやつはCでは未定義かな。

*1:まぁprivateフィールドが無ければできますが