※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

C++いろいろ



コンテナを戻り値にしても大丈夫か?

最もよく使う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;
}