2011-07-28
■[c++] 僕が shared_ptr よりも retain() / release() 方式が好きだと思う理由
それは、shared_ptr のようなラッパーよりも、オブジェクト自体が参照カウンタをもっているほうがコードが書きやすいから。
たとえば、継承したクラスでオブジェクトの参照カウントをいじることは、shared_ptr では難しい。
shared_ptr を使った場合:
class Base {
public:
virtual void foo() = 0;
};
class Derived : public Base {
public:
virtual void foo() {
// ここで自分自身への参照カウンタをインクリメントしたいけどできない…
gManager_.register(this);
};
// 呼び出し側
obj = shared_ptr<Base>(new Derived());
obj->foo();
オブジェクト自体が参照カウンタをもっていれば、このような問題は発生しない。そして、コード量が増えているように見えるけど、参照カウントを管理するための mix-in クラスと CComPtr みたいなラッパーを用意すれば、コーディング量は実質ゼロにできる。
class Base {
size_t refcnt_;
public:
Base() : refcnt_(1) {}
void retain() { refcnt_++; }
void release() { if (--refcnt_ == 0) delete this; }
virtual void foo() = 0;
};
class Derived : public Base {
public:
virtual void foo() {
retain(); // incr refcount to myself and register
gManager_.register(this);
}
};
// 呼び出し側
obj = new Derived();
derived->foo();
だから、「データ型」を扱う場合は shared_ptr でもいいけど、高レベルな処理を記述する「オブジェクト」については retain() / release() でいいんじゃないかと思ってる。
2011-05-20
■nicesort ってのを書いてみた
sleep sort とか環境にやさしすぎて21世紀には不向きだと思うの。なので nicesort なるものを作ってみた。
#! /bin/bash function f() { nice -n "$1" perl -we 'for (1..1000000) {}' echo "$1" } while [ -n "$1" ] ; do f "$1" & shift done wait
こんな感じで動きます。注意点としては、マルチコアだとうまく動かないので taskset 使いましょう (linux の場合) ってのと、0-20の範囲でしかそーとできないよってあたりかしら.
$ taskset 1 ./nicesort.sh 1 9 6 4 1 4 6 9
ちなみに、nice 値が1増えると実行時間は1.25倍になる。これは試験に出るよ!
■Cによるsleep sortの実装がないのもどうかと思ったので書いてみた
Sleep sortの各言語での実装まとめ | Yuyak Blog を見て、Cによるsleep sortの実装がないのもどうかと思ったので書いてみた。シンプルですな。
#include <assert.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char** argv) { int values[] = { 1, 9, 5, 3 }; int i; for (i = 0; i < sizeof(values) / sizeof(values[0]); i++) switch (fork()) { case -1: assert(0); break; case 0: /* child process */ sleep(values[i]); printf("%d\n", values[i]); exit(0); break; default: break; } for (i = 0; i < sizeof(values) / sizeof(values[0]); i++) while (wait(NULL) <= 0) assert(errno == EINTR); return 0; }
■C++0x で sleep sort 書いてみた (was そういえば C++0x での sleep sort ってこんな感じでいいのかな)
GCC 4.6.0 をがんばって OSX に入れたのに std::thread 使えなくて泣きながら修正。こんな感じすね。
#include <cstdio>
#include <vector>
extern "C" {
#include <pthread.h>
#include <unistd.h>
}
int main(int, char**)
{
int values[] = { 1, 9, 3, 6 };
std::vector<pthread_t> threads;
for (auto& v : values) {
pthread_t tid;
pthread_create(&tid, NULL,
[](void* _v) -> void* {
int v = *reinterpret_cast<int*>(_v);
sleep(v);
printf("%d\n", v);
return NULL;
},
&v);
threads.push_back(tid);
}
for (auto tid : threads)
pthread_join(tid, NULL);
return 0;
}
std::thread が使えればこうなる。
int main(int, char**)
{
int values[] = { 1, 9, 3, 6 };
std::vector<std::thread> threads;
for (auto v : values)
threads.emplace_back([v]() {
sleep(v);
printf("%d\n", v);
});
for (auto& thread : threads)
thread.join();
return 0;
}
コンパイラないので試せないわ。
■Re 常識を覆すソートアルゴリズム!その名も"sleep sort"!
常識を覆すソートアルゴリズム!その名も"sleep sort"! - Islands in the byte streamを読んで、自分が書くとしたらこんな感じかなーと思った。多重化して select 使う必要ないよねということで。
use Time::HiRes qw(sleep); sub sleep_sort { # create pipe pipe(my $rfh, my $wfh) or die "pipe failed: $!"; # spawn the processes my @pids; while (@_) { my $value = shift; my $pid = fork; die "fork failed: $!" unless defined $pid; if ($pid == 0) { # child process $| = 1; sleep $value / 10; print $wfh "$value\n"; exit 0; } push @pids, $pid; } # close write handle and swallow all input close $wfh; my @values = <$rfh>; # get rid of the zombie processes for my $pid (@pids) { while (waitpid($pid, 0) != $pid) { if ($! == Errno::EINTR) { # continue } elsif ($! == Errno::ECHILD) { # somehow got reaped (maybe $SIG{CHLD} is 'IGNORE') last; } else { die "unexpected response from waitpid: $!"; } } } # remove \n and return the result chomp @values; @values; }
まあ、wait するくらいなら print する必要もないと思うけどw
sub sleep_sort { my %pid_value = map { +(fork || do { sleep $_ / 10; exit; }) => $_ } @_; my @values; while (%pid_value) { if ((my $pid = wait) != -1) { push @values, delete $pid_value{$pid}; } } @values; }
2011-03-03
■ ファイルに書かれたら irc とかネットに通知するとかそういうデーモンを作るときは mkfifo するといいんじゃないか
デーモン側をこんな感じで書きます。
use Errno qw(EEXIST); use Fcntl qw(S_IFIFO); use POSIX qw(mkfifo); my $FIFO_NAME = "/tmp/my_messenger.fifo"; if (mkfifo($FIFO_NAME, 0666)) { # ok } elsif ($! == EEXIST) { die "$FIFO_NAME is not a fifo!" unless +(stat($FIFO_NAME) & !S_IFIFO) != 0; } else { die "failed to create fifo:$FIFO_NAME, $!"; } while (1) { open my $fh, '<', $FIFO_NAME or die "failed to open fifo:$FIFO_NAME, $!"; while (my $line = <$fh>) { send_message_to_somewhere($line); # どっかに通知する関数 } }
メッセージを送る側は、普通にリダイレクトするだけで OK
% echo hello world >> /tmp/my_messenger.fifo
デーモンのコードの中で FIFO かどうか毎回チェックしてるので万が一ファイル上書いちゃってもデーモン再起動すればOK。というわけで、FIFO いいよという話でした。もうちょっとサンプルコード整理しといた
2011/3/4追記: 通常ファイルを使わずに mkfifo する理由は、通常ファイルだと定期的にトランケートが必要になるけど、そのタイミングで書き込まれたらどうするのという問題(競合)があるから。なのでたとえば crontab で5分かかる処理の結果を /somescript >> /tmp/to_irc みたくして通知しようと思うと通常ファイル + inotify とかじゃダメで mkfifo 一択になる。一方で mkfifo を使う場合は、バッファサイズが 4KB (linuxの場合) なので、通知を受け取って転送する側のプログラムは、できるだけ早くバッファからの読み込みを行う必要がある(そうしないと最悪書き込み側がブロックする)
