No Such Blog or Diary

«Prev || 1 | 2 | 3 |...| 6 | 7 | 8 |...| 12 | 13 | 14 || Next»

C# でグローバルにフック

通常のキーボードやマウスのフックは DLL にエクスポートしたフックプロシージャを使うのだが,C# (.NET) だとこれがうまく出来ないので通常はグローバルフックが出来ないらしい.しかしながら,Low Level Input であればフックがグローバルになるらしく,これを使えば C# でもグローバルフックが出来るとのこと.実際に Web で見つけたソースを元に SetWindowsHookEx に MH_MOUSE_LL を指定してフックしたらうまくいった.ついでにキーボードも問題なく成功.

あとは Low Level Input のレベルでドラッグ中のオブジェクトを取得できるといろいろありがたい.最悪のばあいマウス座標直下にごく小さなウィンドウを作ってドロップのためのイベントを SetInput で投げてしまえば出来るのだが… ドロップ時に何かエフェクトを表示する振りすれば使えそうな手ではある.

剰余の最適化

2^n で割ったあまりを求めるには 2^n -1 との AND をとる方が一般には速い.んで,コンパイラもそこらへんがわかっているので AND 演算に置き換えてくれるのだが… 以下の二つのプログラム

int main(int argc, char *argv[])
{
    int k = 0;
    for(int i = 0; i < 100000000; i++){
        k = k % 4;
    }
    return k;
}

int main(int argc, char *argv[])
{
    int k = 0;
    for(int i = 0; i < 100000000; i++){
        k = k % 4U;
    }
    return k;
}

では後者のほうが3倍くらい速くなる(倍率は環境によるかもしれないけど).両者のアセンブリコードを比べると

	xorl	%edx, %edx
	movl	$99999999, %ecx
	jmp	L6
	.p2align 4,,7
L5:
	andl	$-4, %eax
	subl	%eax, %edx
	decl	%ecx
	movl	%edx, %eax
	js	L10
L6:
	testl	%edx, %edx
	movl	%edx, %eax
	jns	L5
	leal	3(%edx), %eax
	andl	$-4, %eax
	subl	%eax, %edx
	decl	%ecx
	movl	%edx, %eax
	jns	L6
L10:
	leave
	ret

	xorl	%ecx, %ecx
	movl	$99999999, %edx
	.p2align 4,,15
L5:
	movl	%ecx, %eax
	andl	$3, %eax
	decl	%edx
	movl	%eax, %ecx
	jns	L5
	leave
	ret

のようになっており(gcc3.4.4),後者のほうが分岐が無いし速いのは当たり前.とりあえず問題点は「符号付の剰余は負数のときにめんどくさい」という点にある.その面倒な処理が分岐を必要として遅くなってしまうと.

ということで,負数なんて知らんという場合には明示的に符号なしであることを指定しておくべし.

変なコード?

g++ (GCC) 3.3.5-20050130 (Gentoo 3.3.5.20050130-r1, ssp-3.3.5.20050130-1, pie-8.7.7.1) で以下のコードがエラー無くコンパイルされた.3.4.4 とか VC++ (VS.NET 2005) ではコンパイルできなかったのに…

#include <iostream>
#include <string>
 int main(int argc, char *argv[])
{
    for({std::cout << "Hello?" << std::endl; std::cout << "world?" << std::endl; } 0 ; 0) { std::cout << "loop?" << std::endl; }
    return 0;
}

あまり見ない C++ コード

ひとつ目.switch の後に { } がない(switch の意味が無いが…).return でカンマ区切りの式.throw がカンマ区切りをともなってネスト.

#include <iostream>
int main(int argc, char *argv[])
{
    try {
        switch(argc) case 1 : return std::cout << "no argument!" << std::endl, 0, throw (1,throw 9,2), 1;
    } catch (int k) {
        std::cout << "cought exception: " << k << std::endl;
    }
    return 0;
}

どうやらこのコードでは 9 が投げられるらしい.

二つ目.Pointer to Member Function: .* and ->*

#include <iostream>
struct Base {
    virtual void f() {
        std::cout << "Base! " << std::endl;
    }
};
struct DerivedA : public Base {
    int k;
    DerivedA(int kk) : k(kk) {};
    void f() {  // オーバーライド
        std::cout << "DerivedA with k = " << k << std::endl;
    }
};
struct DerivedB : public Base {
};
int main(int argc, char *argv[])
{
    DerivedA *pobjA, objA(5);
    DerivedA objA2(3);
    DerivedB objB;
  
    typedef void (Base::*f_type)();
    f_type f_base = &Base::f;
   
    pobjA = &objA;
    (objA2.*f_base)();
    (pobjA->*f_base)();
    (objB.*f_base)();
}

なんに使えるのかよくわからないが,とりあえずちゃんとオーバライドした関数も呼ばれる.

C++のコメントを抜き出す by Haskell

C++ のソースからコメントだけを抜き出したい衝動に駆られたのでおもむろに Haskell で実装.やはり簡単なプログラムの実装は Haskell のほうが簡単だ…

 -- Extract C++ comments from stdin
import List
import System
 
 -- for C-style comments, i.e. /* comments... */
judgeC (t,c) (c1,c2,c3) 
    = if t==0 && c2=='/' && c3=='*' then (1,0) 
      else if t==1 && c1=='*' && c2=='/' && c > 2 then (0,0) 
           else (t,c+1)
 
 -- for C++-style comments, i.e. /* comments... */ or // comment... \n
judgeCXX (t,c) (c1,c2,c3) 
    = if t==0 && c2=='/' && c3=='/' then (2,0) 
      else if t==2 && c2=='\n' then (0,0) 
           else judgeC (t,c) (c1,c2,c3) 
 
unfilterComments x judge =
    let dx = (zip3 (" "++init x) x (tail x++" ")) 
        cms = scanl judge (0,0) dx
        cms' = zipWith (\(c1,_) (c2,_) -> c1 > 0 || c2 > 0) cms (tail cms)
    in map (\(x,c) -> x) (filter (\(_,c) -> c) (zip x cms'))
 
main = getArgs >>= 
       (\args -> getContents >>=
        (\x -> putStr (unfilterComments x
                       (if not (args ==[]) && head args == "-c" 
                        then judgeC else judgeCXX))))

OpenC++ のコンパイル

OpenC++Core だと Metaclass のロード部分が省略されてしまっているのでバッククォートを使った PTree の簡単な生成が出来ない.Metaclassをスタティックにリンクしてロード部分を自前で書いたうえで ClassWalker を変更すればどうにかできそうだがそれも面倒なので OpenC++ のコンパイルに再びチャレンジ.とりあえず CVS からとってこれなかったので snapshot で我慢するとして,試行錯誤の末にようやくコンパイルできた.

opencxx-20050912.tar.gz を展開後,configure スクリプトを作ってコンパイルするまでの流れ:改行コードを直して, AM_INIT_AUTOMAKE の引数を直してNEWS ファイルを作って(automake と libtools の古いファイルを消して(-f つけて上書きのほうがよいか)),libtools のファイルを生成して,スクリプト生成(aclocal; autoheader; automake -a; autoconf),コンパイル (./configure; make).

まあ,automake に --foreign つけたら NEWS がいらなかったり,そもそも bootstrap スクリプトを動かせ libtoolize 以下は自動でやってくれるのだが… というか,改行コードの修正後に bootstrap で呼び出す automake と libtoolize に -f つけるだけってのが一番楽か.最終的に,autoreconf -vif と実行するのが一番スマートだということに気づいた

とりあえず以下の環境で確認:(GNU automake) 1.9.6,(GNU Autoconf) 2.59,(GNU libtool) 1.5.22,gcc 3.3.5

自前でやるときのマンドは以下のとおり.

tar xfvz opencxx-20050912.tar.gz
cd opencxx
for fname in `find . -regex '.*\.\(am\|in\|m4\|sh\)'` bootstrap; do mv $fname $fname.old && (nkf --unix $fname.old > $fname); done
mv configure.in configure.in.old && (sed -e 's/AM_INIT_AUTOMAKE($PACKAGE, $VERSION)/AM_INIT_AUTOMAKE($PACKAGE, $VERSION, no-define)/' configure.in.old > configure.in)
mv opencxx/main-con.cc opencxx/main-con.cc.old && (sed -e 's/static char thisVersion\[\] = VERSION;/static char thisVersion\[\] = PACKAGE_VERSION;/' opencxx/main-con.cc.old > opencxx/main-con.cc)
mv occ2.in occ2.in.old && (sed -e 's/\*\.cc|\*\.mc)/\*\.cc|\*\.mc|\*\.cpp)/' occ2.in.old > occ2.in)
touch NEWS
autoreconf -vif 
./configure
make

autoreconf を使わなくても以下のコマンドで代用できると.

#rm -Rf missing install-sh mkinstalldirs libltdl depcomp
libtoolize --ltdl --copy -f
aclocal
autoheader
automake -a --include-deps --copy -f
autoconf

とりあえず,examples が動くかどうかを確認してうまくいけば次の仕事に移れるかも…

«Prev || 1 | 2 | 3 |...| 6 | 7 | 8 |...| 12 | 13 | 14 || Next»
Search
Feeds

Page Top