コンテナを戻り値にしても大丈夫か?
最もよく使うstlのvectorで実験してみる。
class Array : public vector<int> {
public: Array() {
push_back(0);
printf("コンストラクタ[%p, %p]\n", this, &at(0));
}
public: Array(Array& arr) : vector(arr){
printf("コピーコンスト[%p, %p]\n", this, &at(0));
}
public: virtual ~Array() {
printf("デストラクタ [%p, %p]\n", this, &at(0));
vector::~vector();
}
};
Array Sub(void) {
Array arr; // スタックにインスタンスを作って
return arr; // それをそのまま返してみる
}
void Run(void) {
puts("IN");
Array arr(Sub());
puts("NEXT");
arr = Sub();
puts("OUT");
}
int _tmain(int argc, _TCHAR* argv[]) {
Run();
getchar();
return 0;
}
実行結果
IN <-- RUNに入ったとき
コンストラクタ[0012FD3C, 0039A6E0] 確保
コピーコンスト[0012FE6C, 0039A720] : 確保
デストラクタ [0012FD3C, 0039A6E0] 解放 :
NEXT <-- :
コンストラクタ[0012FD3C, 0039A6E0] 確保 :
コピーコンスト[0012FD8C, 0039A760] : : 確保
デストラクタ [0012FD3C, 0039A6E0] 解放 : :
デストラクタ [0012FD8C, 0039A760] : 解放
OUT <--RUNから出るとき :
デストラクタ [0012FE6C, 0039A720] 解放
もしかして中身はそのままでアドレスのみコピーかと思ったが、まるごとのコピーが発生する。
ただし、動作に問題はない。メモリリークは発生しない。
今度は、shared_ptrで実験してみる。mainとclass Arrayはさっきと同じ
boost::shared_ptr<Array> Sub(void) {
boost::shared_ptr<Array> p = boost::shared_ptr<Array>(new Array());
return p;
}
void Run(void) {
puts("IN");
boost::shared_ptr<Array> parr(Sub());
puts("NEXT");
parr = Sub();
puts("OUT");
}
実行結果
IN <-- RUNに入ったとき
コンストラクタ[00348BB8, 00348C10] 確保
NEXT :
コンストラクタ[00344C48, 00344CA0] : 確保
デストラクタ [00348BB8, 00348C10] 解放 : <--!!
OUT <-- RUNから出るとき :
デストラクタ [00344C48, 00344CA0] 解放
見事に理想通りの動作をした。
parr = Sub()の行で、最初に確保したArrayを指すものがなくなると
きちんと解放されている。
shared_ptr<array>は、typedefしておこう。
8bit×4パックド飽和加算
いまさらですが、32ビット変数を用いて8ビット×4のパックド飽和加算をしてしまう!というコードを検証してみました。
意外なことに、テストをパスできたコードがありませんでした(@@
いまどき、SSE2の使えないCPUを使うことはほとんどありませんが、組み込みでは非インテルなこともあるので、残念な結果になりました。
//(Haya様HPより)
// 合計が256になったとき、0になってしまう(255が正解)。 時々、合計が1多くなる
Int32 AddBlend(Int32 a, Int32 b) {
Int32 m = (0x80808080
- ((((a >> 1) & 0x7f7f7f7f) + ((b >> 1) & 0x7f7f7f7f)
+ ((a ^ b) & 0x01010101)) >> 7 & 0x01010101)) ^ 0x80808080;
return (a & (~m)) + (b | m);
}
//(Kouhei Yanagita様HPより)
// 最上位バイトが飽和せず、折り返してしまう
Int32 AddBlend(Int32 a, Int32 b) {
Int32 c = ((a & b) + (((a ^ b) >> 1) & 0x7f7f7f7f)) & 0x80808080;
Int32 m = (c << 1) - (c >> 7);
return ((a + b) - m) | m;
}
// (やねうらお様HPより)
// 最上位バイトが飽和せず、折り返してしまう
Int32 AddBlend(Int32 a, Int32 b) {
Int32 c = ((( a & b )<<1) + ((a ^ b) & 0xfefefe)) & 0x1010100;
Int32 m = c - (c>>8);
return (a + b - c) | m;
}
// テスト親プログラム
int main(array<System::String ^> ^args) {
Byte array32s1[4] = {1,2,3,4};
Byte array32s2[4] = {3,4,5,6};
Byte array32sa[4];
Random^ rand = gcnew Random();
int errcnt = 0;
for (int i = 0 ; i < 10000 ; i ++ ) {
// 初期値を入れる
for (int j = 0 ; j < 4 ; j++ ) {
array32s1[j] = rand->Next(0,255);
array32s2[j] = rand->Next(0,255);
}
// 実行
*(reinterpret_cast<Int32*>(array32sa)) =
AddBlend(*(reinterpret_cast<Int32*>(array32s1)),
*(reinterpret_cast<Int32*>(array32s2)));
// テスト
int isTestError = 0; // ==false
for (int j = 0 ; j < 4 ; j++ ) {
int a = array32s1[j] + array32s2[j];
if (a > 255) a = 255;
if (a != array32sa[j]) isTestError |= (1<<j);
}
if (isTestError) {
Console::WriteLine( i.ToString("d5")
+ "------------------------");
for (int j = 0 ; j < 4 ; j++ )
Console::Write(" "+array32s1[j].ToString("d3")+" ");
Console::WriteLine();
for (int j = 0 ; j < 4 ; j++ )
Console::Write(" "+array32s2[j].ToString("d3")+" ");
Console::WriteLine();
for (int j = 0 ; j < 4 ; j++ ) {
if (isTestError & (1<<j)) {
Console::Write("<"+array32sa[j].ToString("d3")+"> ");
errcnt ++;
} else {
Console::Write(" "+array32sa[j].ToString("d3")+" ");
}
}
Console::WriteLine("");
}
}
// 結果表示
if (errcnt==0)
Console::WriteLine("pass!");
else
Console::WriteLine("error = " + errcnt.ToString() );
Console::ReadLine();
return 0;
}
ほかにもこんなのが掲載されていました。(未検証)
// (やねうらお様HPより)
// 平均
Int32 Ave(Int32 a, Int32 b) {
return (a&b) + (((a^b) & 0xfefefefe) >> 1);
}
// 飽和インクリメント
Int32 Inc(Int32 a) {
Int32 num = ((~(a & ((a & 0x7f7f7f7f) + 0x01010101 ))) & 0x80808080) >> 7;
return a + num;
}
// 飽和デクリメント
Int32 Dec(Int32 a) {
Int32 num = ((x | ((x | 0x80808080) - 0x01010101)) & 0x80808080 >> 7;
retrun a - num;
}
// 比較
Int32 Cmp(Int32 a, Int32 b) {
Int32 c = a^b;
c = (((c & 0x7f7f7f7f) + 0x7f7f7f7f) | c) & 0x80808080;
c |= c - (c >> 7);
retrun ~c;
}
// 左シフト(16ビット)
Int32 ShlW(Int32 x, Int32 s) {
Int32 m = 0xffff0000 ~ (0x0000ffff < s)
}
整数の絶対値
inline Int32 fastabs(Int32 a) {
Int32 m = a >> 31;
return (a ^ m) - m;
}
2整数のmax, min
inline Int32 fastmax(Int32 a, Int32 b) {
Int32 t = (a-b);
return a - (t & (t >> 31));
}
inline Int32 fastmin(Int32 a, Int32 b) {
Int32 t = (a-b);
return b + (t & (t >> 31));
}
16進文字列を数値にする
string str = "0xcdef";
int a = 0;
try {
a = boost::lexical_cast<int>(str);
}
catch(boost::bad_lexical_cast&) {
;
}
構造体メンバの先頭からのオフセットを得る
#include <stddef.h>
size_t offsetof( type, member);
コールバックの実装(Boost)
Runメソッドを呼ぶと、testEventに登録された関数が呼ばれる。
testEventメンバをpublicにせず、SetTestEventメソッド経由にする理由は、外部からイベントハンドラを呼ぶことが出来てしまうからである。SetTestEventメソッドの引数にNULLを渡すと、イベントハンドラをクリアする事が出来る。
// 呼び出し元
class aa {
// イベントハンドラ(イベント発生時に呼び出す関数のアドレス)
protected: boost::function<string (int a,int b)> testEvent;
// 内部からイベントハンドラを呼び出す為のラッパー
protected: virtual string OnTestEvent(int a, int b) {
string retstr;
if (testEvent!=NULL)
retstr=testEvent(a,b);
return retstr;
}
// イベントハンドラを登録する
public: void SetTestEvent(boost::function<string (int a,int b)> func) {
testEvent = func;
}
public: void Run(void) {
cout << "[" << OnTestEvent(5,8) << "]" << endl;
}
}
// 呼び出し先
class bb {
protected: string TestEventFunc(int a, int b) {
return boost::io::str(boost::format("a+b=%d") % a+b);
}
}
//
int main(int, char**) {
aa AA;
bb BB;
AA.SetTestEvent( boost::bind(&bb::TestEventFunc, &BB, _1, _2);
AA.Run();
return 0;
}
最終更新:2009年02月02日 21:52