忘れないうちにメモをしろ 2003-07〜


 プログラミングにまつわることで、思い出したこと、考えたこと、気になること など、ともかく低レベルでもみっともなくても中途半端でいいからメモを取ろう、 と思うページ(忘れてしまって困るのは己なのだ)... なんだが、すっかり忘れはててるなあ、このページ自体^^;

※ ろくにチェックせずにアップしとりますんで、鵜呑みにせず、ソースもバグってるかも で気いつけたってください。


 2003-07-28 ひさしぶりに...


 久しぶりに趣味で小物ツールをC++の練習がてら作ってみた。
 仕事では、なんだかんだいってターゲットマシン用のソースを c で 書いているせいか、いつまでたってもC++は心元ない状態なので、 それはまあ自分でも面白くなく... てのもあるし、 次の(新規)仕事はターゲット用もc++で書いていいかなあ、ということになりそうなんで (少なくとも gcc/vc/cw あたりならそこそこコンパイラ信用できるだろし)。 まあ移植元がCソースだと、Cするしかないってものあったのだけど。

 作ったのは、c/c++ソースをhtmlにするコマンドラインツール ...なんか不毛な気もするが気にしない^^;)
 で肝心のc++の練習としては、あまりよろしくないソースに。 結局 c での癖が出まくりで。いろいろCのルーチン持ってきてるせいもあるけど。 cなら、すでに分かって出来ることを、 c++だとわからないから調べなければならず...調べ切れず、てかすぐ面倒がって挫折。

 例えば、c だと、

    fp = (name) ? fopen(name,"wt") : stdout;
みたいなマネをして、標準入出力とファイル入出力の本体は共有化するけど、 c++ だと、cout のクラスと, ofstream のクラスが違うので、さてどうしたものか、と。 こんなんみかけたり、で...いやクラス違うったって継承してるからいけるはずだけど... あれ、あ、いや、なんか勘違いしてるだけか?
    1 : #include <iostream>
    2 : #include <fstream>
    3 : using namespace std;
    4 : 
    5 : int main(int argc, char *argv[]) {
    6 :     ofstream ofstrm("tmp.txt");
    7 :     ostream  &os = (argc < 2) ? cout : static_cast<ostream&>(ofstrm);
    8 :     os << "test test test\n";
    9 :     return 0;
   10 : }
お、あ、ちゃんと動く...
うう、ひょっとすると前は ofstream os = ... とか ostream os((argc < 2) ? cout : ofstrm); とかやってもーてダメだと思い込んだのか...ああと 前はfreopen()と同様なことしようと思ってどうしようとかで 悩んでたのとゴッチャになってもたか ...非情に間抜け...ちょっとダメすぎです>己(かなり落胆)。

と、まあ、まだまだ勉強中の身なのです。


2003-08-08 追記メモ

またもヘマ。上記例の 7行目の ostrm にキャストが抜けていたので追加。 vcやbccでは怒られるのだった... うう、前試したときは動いたと思うが..何かが違ったのかヘマったのか... なぜかg++ではコンパイルできるけど前回は試してないはずやし...はあ(落胆)




 2003-07-30 new[] のアドレスの怪


 いや、怪ってわけじゃないけれど。

 ターゲットマシン向けに使うの前提で、自前でmalloc 系ルーチンを用意したりする。 DMA対策でアライメント(アドレスを32や64の倍数にする)管理とか、 有限のメモリなんでポインタの整合性(範囲)チェックを強化したりとか、 メモリ取得のアルゴリズムが分かってるのでメモリ分断の対策と考えやすいとか。

 で、まあ、それを用いて、winでvc++で試していると、なぜかアライメントチェックに ひっかかってしまったのだった。よくよく見ると、new[] している箇所。 てことでvc7,bcc32(5.5.1)で以下のようなお試し。

// new[] で取得したメモリのアドレス確認
//#include <iostream>
#include <stdio.h>

struct Strct {
    double  x,y,z,w;
};

struct Strct2 {
    double  x,y,z,w;
    Strct2()  {x = y = z = w = 0.0;}
    ~Strct2() {x = y = z = w = 0.0;}
};

int num = 11;
//enum {num = 11};

int main(void)
{
    printf("型       new内部  取得アドレス\n");
    printf("double[] ");
    double* dbl = new double[num];
    printf("0x%x\n", dbl);

    printf("Strct[]  ");
    Strct* strct = new Strct[num];
    printf("0x%x\n", strct);

    printf("Strct2[] ");
    Strct2* strct2= new Strct2[num];
    printf("0x%x  (%d)\n", strct2, *((int*)strct2-1));
    return 0;
}

void *operator new(size_t sz)
{
  #if defined(_MSC_VER) // VCでのアドレスのアライメント
    __declspec(align(256))
  #endif
    // 比較しやすいようにアドレス固定
    static double m[64*1024];
    //void *m = malloc(sz);
    printf("0x%x ", m);
    return m;
}

void *operator new[](size_t sz)
{
    return operator new(sz);
}
固定のアドレスを返す new を用意して、取得するアドレスを確認。
結果は
vc7 (cl v13.0)
型       new内部  取得アドレス
double[] 0x40bb00 0x40bb00
Strct[]  0x40bb00 0x40bb00
Strct2[] 0x40bb00 0x40bb04  (11)

bcc32 (v 5.5.1)
型       new内部  取得アドレス
double[] 0x40c57c 0x40c57c
Strct[]  0x40c57c 0x40c57c
Strct2[] 0x40c57c 0x40c580  (11)

 どうやら、デストラクタ付きのクラスを確保したときは、4バイトアドレスがズレる。 増えた中身を確認すると配列の個数の模様...
考えてみれば、当然だわな。deleteのタイミングにデストラクタを起動すべき 個数情報がないと困るもの。

 まあ当初 new[] なら必ず 個数情報がつくんかな、と思ったから意外と賢いもんだなあ と思えたり。(いや別に仕組み的に難しくはないか)。

 しかし、折角、class/structの中のアライメントを気にして配置しても、デストラクタがあると new[] では 4バイトづれちゃう、てのは... やっぱ、がっかりやなあ。
win(vc,bcc)では new[] せず vector<T> しちゃうのがええんかも。new[]禁止か>己。

 ちなみに、gcc(mingw) だと 16バイトずれてたと思う(今手元にgcc環境がないのでうろ覚え) 8バイトずれる模様(後述).
他の(アライメントずれたら速度だけでなくプログラム自体が落ちるような)CPU 向けのコンパイラだと、その辺はキチンと考慮して 4バイト(size_t) でなく CPUの困らない単位でずれてそうだし、あるいは Hitachi-C なんかだと 予めmallocのヘッダ部分にC++用の予約領域をとったりしてるようだし (あ、new[]用かは未確認だけど状況的にそうだろと)。

※malloc ヘッダで対応って、結構いい手なんだよなあ、と思えたり。 教科書どおりのmallocルーチンを 使う場合、ヘッダは最小ブロック(セル)サイズになるので、ハード構成によって 1ブロック 32バイトや64バイトだと空きが有効活用できるし (ちょっと工夫すれば、ヘッダサイズは必要サイズ丁度ですむ話だけれど)。
言語仕様が new専用の malloc を前提に制定されていたならば、 delete[] なんて[]をつける無粋なことせんですむのに、って思う面もあるし (でもホントにそうなってたら、微妙に個数チェックが増えるのがイヤとか言ってそうだ>己)。


 と、まあ、この件自体は、わかってしまえば大したことないんだが、 上記サンプルには別の怪があって^^;
 コメントにしている先頭の #include <iostream> をアリにしたときと、 コメントアウトにしたときで、 operator new[](sz) が呼ばれるか、operator new(sz) が呼ばれるかが 代わってしまうのだった。サンプルでは、結局 operator new[] とoperator new を 同一にすることでごまかしたのだけど^^; (これも単なるヘマ、無知、だろうなあ)



2003-08-02 追記メモ

結果の表示として printf を使ったけれど、実は最初 cout で 書いていたのだった。operator new の中も含め... で、 main()に到達する前にハングする羽目に。 cout のどこかでoperator new 呼び出してるんだろうなあ、 ってわかってみれば当たり前のような気もするけど最初は気づかないもんだな^^;。 つーことで、この手のローレベルな(内部よりな)ことする場合は 極力余計なことしない/影響の少ない関数でデバッグせな不味いわな、で、 printfを使うことにしたのだった。




 2003-08-02 アライメント


 new[] の件から、ふと、virtual関数のあるクラスの場合のアライメントが気になったのだった。
 仮想関数のあるクラスだと、暗黙に仮想関数テーブルへのポインタをメンバ変数に持つわけで、 そういう場合のアライメントは期待したようになっているんかいなあ、と... コンパイル時に決定できるんだから大丈夫だろうとは思うんだけどね。
 仮想関数のあるクラスのメモリ増加/配置については こちら「sizeofの不思議」 に詳しく書かれているので、そちらを参考にして、と。 とりあえず、単一継承のみで、軽くお試し。

#include <stdio.h>
//#define __declspec(x)
#define TSTMSG(s,t,top) printf("%s> %#x %#x %2d\n", (s), (t), (top), (char*)(top) - (char*)(t))

struct Clss1 {
    double       top_;
    void         disp() {TSTMSG("virtual無", this, &top_);}
};

struct Clss2 {
    double       top_;
    virtual void disp() {TSTMSG("double   ", this, &top_);}
};

struct Clss3 {
    int          top_;
    virtual void disp() {TSTMSG("int      ", this, &top_);}
};

struct Clss4 {
    __declspec(align(32)) double top_;
    virtual void disp() {TSTMSG("align(32)", this, &top_);}
};

Clss1 clss1;
Clss2 clss2;
Clss3 clss3;
Clss4 clss4;

int main(void) {
    printf("           this     &top_    差\n");
    clss1.disp();
    clss2.disp();
    clss3.disp();
    clss4.disp();
    return 0;
}
結果は、vc7(cl -GX )で
           this     &top_    差
virtual無> 0x408720 0x408720  0
double   > 0x408788 0x408790  8
int      > 0x408780 0x408784  4
align(32)> 0x408740 0x408760 32
で、差(サイズ)を見ると期待したようにアライメントされてる模様。
 じゃあ、とフリー配布なbcc32(5.5.1) で align(32)以外 を試すと
           this     &top_    差
virtual無> 0x40c58c 0x40c58c  0
double   > 0x40a128 0x40a12c  4
int      > 0x40a138 0x40a13c  4
align(32)> 0x40a140 0x40a144  4
...あれ、4バイト固定?。 ひょっとして、と、 #pragma pack(16) を足したりコンパイルオプションで -a8や-a16 とアライメント指定して みたり、-Oxでオプティマイズ指定したけれど、変わらず。
 ということで 4バイト固定の模様。 通常のメンバー部分でのアライメントは、継承の有無に関わらず行われるようなので、 継承するとズレちゃう、ってことのようですね...。

 あと、class/struct問わず、割り当てられた変数のアドレスについて、 cl はアライメント大丈夫そうにみえるけど、bcc32のほうは期待した通りに ならない場合もある模様。
 virtual無しのアドレスや、先の new[] の件の bcc32 での結果をみれば、 アドレスが8(sizeof(double))の倍数になっていなかったりする。 関数内だから駄目なんかと、関数外にしたり static をはずしたり、しても駄目、 コンパイルオプションで -a8 としても駄目... struct/classなら大丈夫なのか(たまたまなのか)、 何か設定抜けてるのか、結局このbcc32ではアライメント関係は 当てにならんのか...うう面倒になってきたので考えるの放棄^^;

 ちなみに上記サンプル、 当初 class 変数を関数内でstaticでないローカル変数として定義しちゃってたら、 cl / bcc32 ともに、 (アライメント関係のオプション指定をしても) 先頭アドレスがちっともアライメントされず 結構焦ったのでした。
 まあ、スタックに取られるローカル変数のときくらいはしゃーないと思うしか、x86だし... 過去との互換なんかな?... アライメントミスが致命傷なCPUではちゃんと行われることなんだから、やってほしいけれど。




 2003-08-02 アライメント(2)


 気になったので gcc(mingw) をインストールしてお試し。
 お手軽な dev-cpp (4.9.8) をインストールなんで gcc v3.2。 IDE環境設定でいろんな国対応してるけどJapaneseないや、 で、ちょっと残念に思うも目当てはコマンドラインなんで とりあえず、気にしない、でおこう。

 c:\dev-cpp\bin をpathに追加して gcc test.cpp を実行... でコンパイル通るも new 関係のなんかでリンクエラー。ありゃりゃ。 ライブラリ指定せちゃならんのか... どうすんべ、と、dev-cpp の プロジェクト生成で 出来たmakefileを見てみれば、なんのことはない、 g++.exe を使うのね。
(なんか都合変わったんかなあ、と、思うも大体自分が いままでロクにc++使ってなかっただけってことだ。)

とりあえず、お試しの結果。 new[] の件は、

型       new内部  取得アドレス
double[] 0x40e020 0x40e020
Strct[]  0x40e020 0x40e020
Strct2[] 0x40e020 0x40e028  (11)
アライメントの件は
           this     &top_    差
virtual無> 0x40e020 0x40e020  0
double   > 0x40e028 0x40e030  8
int      > 0x40e038 0x40e03c  4
align(32)> 0x40e040 0x40e060 32
で、想定/希望どおりの結果のよう。 new[] でのアドレスの差が 8バイトなんで、 double や __int64(long long) での都合も大丈夫なんで安心だなあ。 関数内のローカル変数でのアライメントも、align(32)の指定は無視されたけど、 一応、double を考慮したように、8の倍数のアドレスになっていた。



と、試すにあたって、__declspec(align(32)) を書き換えなきゃいけなかったのだけど、 どうも、ある程度の対応が gcc 側に入ってきてるみていですね... gcc ってより mingw(あるいはwin系gcc) 環境だけかな。

 bcc32 と同様にとりあえず #define __declspec(x) をして取っ払おうとしたら、 redefined error が出て。 一瞬文法増えたかと期待したけど redefined だからマクロエラーだよなあ。 include\ を検索すると windef.h 中で __attribute__ 関係で置き換えをしてる...

 なるほどっ、と、いうか、__attribute__ って前置できるんですか?! とびっくりしたり。後置のみかと思ってので...拡張されたのかな? 前からなんやろか。そうだとしたら己の間抜けにまた泣きそ。
後置のみと思って、

#if defined(_MSC_VER) && _MSC_VER >= 1300
#define ALIGNED_DECL(al, ty_nm)     __declspec(align(al)) ty_nm
#elif defined _GNUC_
#define ALIGNED_DECL(al, ty_nm)     ty_nm __attribute__((aligned(al)))
#else
#define ALIGNED_DECL(al, ty_nm)     ty_nm
#endif
のようなマクロを用意したこともあるんで。 (例えば ALIGNED_DECL(32, char buf[1024]); で、あまり見栄えのよくない、 他人の受けの悪いソースになってしまうんで、ほぼ使わずだけど)

 まあ現状だと align(32) が怒られちゃうんで、お試しでは

  > g++ -Dalign=aligned test.cpp
のように誤魔化してソース編集無しで済しましたが、 #define align(n) aligned(n) をライブラリのヘッダ中でやると、 弊害がおきやすそうな気もするので、やらないのだろうなあ。

自分のソースだけなら

#if defined(_MSC_VER) && _MSC_VER >= 1300
#define ALIGNED(al)     __declspec(align(al))
#elif defined _GNUC_
#define ALIGNED(al)     __attribute__((aligned(al)))
#else
#define ALIGNED(al)
#endif
のようにして、 ALIGNED(32) char buf[1024]; のように書けるだけでもよさげ...

と、と、と。よくよく考えてみれば、(仕事で)使わざる得ない古いバージョンの (多少はgcc互換の拡張文法のある) CodeWarrior でこの手が使えないとありがたみがあまりなく... で、調べてみたら__attribute__ は後置のみでした(泣)
(つうことは、昔はgccも後置のみだったんだろうな、で、少しホッ^^;)


ついでメモ。

vc7(cl)での、コマンドラインオプションでのアライメント指定方法は、-ZpN
bcc32での、コマンドラインオプションでのアライメント指定方法は、-aN
(N は1,2,4,8,16 )

vc(mc),bccでの構造体のアライメント(バウンダリ)お指定は、昔から
#pragma pack(N)
#pragma pack(push, N)
#pragma pack(pop)
なんかがあってbcc,vcの互換で言えば、__declspecなんぞ使わず、こちらを使うほうがほんとはいいんだよなあ。 けど、個人的に #pragma って嫌いなんで... #defineマクロで辻褄合わせできる可能性がなくなるのが^^;


 希望としては、用途や cpu アーキテクチャの現状を思うと、 いいかげんアライメントの指定方法や、エンディアンの指定くらい (ついでにビットストリーム・ポインタとかも) c/c++ の文法に含めてほしいなあ、とかなり思ってしまう。
template で結構できそうなものもあるけど、効率を思うとネイティブに 組みこまれてほしいのねん。

趣味的には、c言語以上に低レベル記述に適したアセンブラの代用になるような (俺)言語を作ってみたくなる面もあるけど:-)...と妄想はおいといて。

ほんま標準で決まってくれていたら、みんな楽やのになあ。





 2003-08-03 アロケータを作る


 自前/あるいは標準でない malloc 系ルーチンを使った stl な allocator がほしいなあ、と思うことがあったわけで(ちょっと)。

 使いがってや使用目的/状況からすると自分的には operator new() を オーバーロードすれば大抵コト足りるんだけど、 まあ、出来ないのも癪だし、一見面倒くさそうだけど結構簡単そうだし、で。 (結果は、結構時間取られたし、ようわからん部分は適当にして凌いだのだけど^^;)

 作ったものは以下のモノ。汎用的?にするため template class を生成するマクロになっています:-)

/**
 *  @file   gen_allocator.h
 *  @brief  malloc(),free(),max_size() 関数を用いて、stlなアロケータを生成するマクロ
 *  @author Masashi Kitamura <NBB00541@nifty.com>
 *  @note
 *      max_size()は1度のmalloc呼出で確保できる最大バイト数を返す関数、とする。
 */

#ifndef GEN_ALLOCATOR_H
#define GEN_ALLOCATOR_H

#define GENERATE_ALLOCATOR(ALLOCATOR_NAME, MALLOC, FREE, MAX_SIZE)              \
template<class T> class ALLOCATOR_NAME {                                        \
  public:                                                                       \
    typedef T               value_type;                                         \
    typedef T*              pointer;                                            \
    typedef const T*        const_pointer;                                      \
    typedef T&              reference;                                          \
    typedef const T&        const_reference;                                    \
    typedef std::size_t     size_type;                                          \
    typedef std::ptrdiff_t  difference_type;                                    \
    ALLOCATOR_NAME() /*throw()*/ {}                                             \
    ALLOCATOR_NAME(const ALLOCATOR_NAME&a) /*throw()*/ {}                       \
    template<class U> ALLOCATOR_NAME(const ALLOCATOR_NAME<U>&) /*throw()*/ {}   \
    ~ALLOCATOR_NAME() {}                                                        \
    static pointer          address(reference x) /*const*/ {return &x;}         \
    static const_pointer    address(const_reference x) /*const*/ {return &x;}   \
    static pointer allocate(size_type n) {return (pointer)MALLOC(sizeof(T)*n);} \
    static pointer allocate(size_type n,const void *) {return allocate(n);}     \
    static void deallocate(pointer p)               {if (p) FREE(p);}           \
    static void deallocate(pointer p, size_type)    {deallocate(p);}            \
    static void construct(pointer p, const T& val)  {new((void*)p) T(val);}     \
    static void construct(pointer p)                {new((void*)p) T;}          \
    static void destroy(pointer p)                  {p->~T();}                  \
    static size_type max_size()/*const*/ throw() {return MAX_SIZE()/sizeof(T);} \
    template <class U> struct rebind {                                          \
        typedef class ALLOCATOR_NAME<U> other;                                  \
    };                                                                          \
};                                                                              \
template<class T1, class T2> inline                                                         \
bool operator ==(const ALLOCATOR_NAME<T1>& x, const ALLOCATOR_NAME<T2>& y) {return true;}   \
template<class T1, class T2> inline                                                         \
bool operator !=(const ALLOCATOR_NAME<T1>& x, const ALLOCATOR_NAME<T2>& y) {return false;}

#endif
用意する関数は、 の3つ (てか、ありあわせのcなmalloc/freeルーチンをallocator化する、てのが趣旨なんで)。
使い方は、
inline unsigned max_size() {return 0xffffffff;}
GENERATE_ALLOCATOR(my_allocator, std::malloc, std::free, max_size);
な感じですかね。my_allocator という template class が作られます。
例での max_size() は、 ライブラリ/OSによっては 標準関数のmalloc,freeに対応した関数があったりしますが 調べんのが面倒なのと os まかせで信用してデッカイ値にして逃げてます^^;

 GENARATE_ALLOCATOR マクロの手抜き、 としては、oparator == と != の扱いがピンときてないので適当にしちゃってます。
(その他も作ってからだいぶ経ってるせいか今見ると、結構怪しいかもって気にもなるなあ..  あ、== と != は逆の真偽を返したほうがよいのかも...)

 ついでに。最初作ったときにはまった(てほどでもないか)のは、rebind の定義を要求されたことでした。 どうも、この定義が必須ぽいようで。
作成時の元ネタの(てか普段から利用させてもらってる) こちらallocatorの解説に 載ってなかったからなんですが(頁の趣旨からすれば載って無くてよいんだけど)、 各コンパイラのヘッダみると書いてあるので真似たのでした (で、後で「STL標準講座」を見直すとちゃんと言及されててトほほな気分になった、と^^;)。



 あと、適当なテストルーチン例。

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <map>
#include "gen_allocator.h"
using namespace std;

inline void *tst_malloc(size_t sz) {
    void *m = malloc(sz);
    printf("\t%#x = malloc(%#x)\n", (unsigned)m, sz);
    return m;
}

inline void tst_free(void *p) {
    printf("\tfree(%#x)\n", (unsigned)p);
    free(p);
}

inline size_t tst_msize() {
    size_t sz = 0x7fffffff; // 手抜きで大きな値を返しておく(osまかせ)
    printf("\tmax_size()\n");
    return sz;
}

GENERATE_ALLOCATOR(tst_allocator, tst_malloc, tst_free, tst_msize);

int main(void) {
    //typedef string  TstString;
    typedef basic_string<char, char_traits<char>, tst_allocator<char> > TstString;
    typedef map<int, TstString, less<int>, tst_allocator<int> >         MapIntStr;
    MapIntStr m;
    TstString s("");
    m[1]    = TstString("test1 ");
    m[500]  = s = TstString("test500 ");
    m[9999] = TstString("test9999 ");
    cout << m[1] << m[500] << m[9999] << endl;
    return 0;
}

...うう、やっぱ allocator を指定するのは、面倒だよなあ。 大抵、最後に指定するから、map にしろ basic_string にしろ 途中の引数を指定しないといけないし(覚えてないので調べるはめになるし)、 見た目も長くなって分かりズらいし、で...

ま、とりあえず、 vc,gcc,bcc での結果。

(1) typedef string TstString;の場合

vc gcc bcc
	0x341048 = malloc(0x30)
	max_size()
	0x341080 = malloc(0x30)
	max_size()
	0x3410b8 = malloc(0x30)
	max_size()
	0x3410f0 = malloc(0x30)
test1 test500 test9999
	free(0x3410f0)
	free(0x3410b8)
	free(0x341080)
	free(0x341048)
	0x492ca0 = malloc(0x18)
	0x492cf0 = malloc(0x18)
	0x492d18 = malloc(0x18)
	0x492d40 = malloc(0x18)
test1 test500 test9999
	free(0x492d40)
	free(0x492d18)
	free(0x492cf0)
	free(0x492ca0)
	0x883c3c = malloc(0xc)
	0x883c4c = malloc(0x28)
	0x883c90 = malloc(0xc)
	0x883ca0 = malloc(0x690)
test1 test500 test9999
	free(0x883ca0)
	free(0x883c90)
	free(0x883c4c)
	free(0x883c3c)

(2) 自前のallocator で TstString にした場合

vc gcc
	0x341048 = malloc(0x30)
	max_size()
	max_size()
	max_size()
	max_size()
	0x341080 = malloc(0x30)
	max_size()
	max_size()
	max_size()
	max_size()
	max_size()
	0x3410b8 = malloc(0x30)
	max_size()
	max_size()
	max_size()
	max_size()
	max_size()
	0x3410f0 = malloc(0x30)
	max_size()
	max_size()
	max_size()
	max_size()
	max_size()
test1 test500 test9999
	free(0x3410f0)
	free(0x3410b8)
	free(0x341080)
	free(0x341048)
	0x492ca0 = malloc(0x18)
	0x492cf0 = malloc(0x18)
	0x492d18 = malloc(0x13)
	0x492d40 = malloc(0x18)
	0x492d68 = malloc(0x15)
	0x492d90 = malloc(0x18)
	0x492db8 = malloc(0x16)
test1 test500 test9999
	free(0x492db8)
	free(0x492d90)
	free(0x492d68)
	free(0x492d40)
	free(0x492d18)
	free(0x492cf0)
	free(0x492ca0)

 あまし意味のないテストルーチンなんで、コンパイル通って それなりの値になってたらいいや、でしたが、 図らずもSTLライブラリの実装の違いが出たようで、 ちょっと楽しめたかも。
(ちなみに vc7 は Dinkumwareベース、 gcc(3.2) は STLPort ベース、 bcc(v5.5.1) は Rogue Wave ベース ってコトでいいんかな)。

 vcは、max_size()の回数は多いが stringを使っても この程度の使用だと malloc せずに済ませてるけど、 gcc だと max_size() を使ってなさそうだけど stringで malloc が呼び出されてる、と。

 bcc32 は、(2)のTstStringを用意した場合、 コンパイルは通るけどリンクエラーで実行できなかったで、 あきらめちゃいました。 〜::__nullref や 〜::__getRep 等いくつか未解決の外部シンボルが ある、って。
 先の必須ぽいと書いてしまった rebind だけど 「STL標準講座」には “次の構造体とともに、void*ポインタの特殊化されたバージョンが定義されています” と前書きしてあったのを思い出すと __nullref とかはこの件の実装違い、のような気もしてきたり、 でも rebind は bcc のでも他のコンテナでは使われてるぽいので 単にコンパイラの性能やライブラリ仕様の問題ってことなんでしょうかね... いや、単に、己の書いたアロケータに必要な定義が足りてないってのが 濃厚なんですが^^;




2003-08-03 追記
 型列挙無しの throw() 指定が、例外を返さないって意味なのを失念して、 無茶、お間抜けなこと書いていたのを削除&ソース修正...ううう恥ずかし。

 で、定義みてソース修正するも、空関数状態のコンストラクタに throw() つけると " 例外リストを持つ関数はインライン展開されない" とbcc32に怒られたのでそれはコメントアウト. 実質意味あるのは max_size() だけなんでちゃんと実情に合わせて定義しろって、 ことなのね...
 そいや 関数の const 指定も、直接メンバー変数にアクセスするはずがない static メンバー関数には付けれないようで、vc,gcc,bccともに怒られてしまい コメント化したのでした (実は1年以上前に書いたソースで、そのときはコンパイル通ってたはずなんだが...不思議^^;)。

 ついでに。GEN_ALLOCATOR で作られるクラスはメンバ変数を持たないので、 まあこれに限らずメンバ変数を持たない class の sizeof はいくらになるのだろうか、と、
sizeof(tst_allocator<char>), sizeof(tst_allocator<int>), sizeof(tst_allocator%lt;double>)
を表示してみたんですが、結果は

    vc  は 1,1,1、
    gcc は 1,1,1、
    bcc は 8,8,8、
非0であることだけ、保証するって感じなんですかね。


2003-08-04 追記

 constract() の定義での new(p) T を new((void*)p) T に変更しました。
operator new は再定義できちゃうので、どっちみち不味いような気もするけど、 せめてoperator new(size_t,FooType*)のような個別の型での オーバーロードにかぶらないよう、 でもって少なくとも operator new(size_t,void*)はデフォルトで運用するだろう、と 期待/前提して...なんか考え違えしてるような気もしてくるけれど。
 しかし、なんで charやint等の組み込み型って p->~T() が許されて、 p->T() はダメなのよ(T T)。
vc,bccと違い gcc は通すみたいだけど...未来の規格案なのか独自仕様なのか... ARMの350P付近見る分にはnew(p)なんだろうな、でとりあえず。
 ああと。最近読んでる「Modern C++ Design」をみてると、 頑張れば基本型を見分けてnew()せずに記述できそうにも思えてきますが... 己の把握限界を超えた世界なので諦めます(T T)



 2003-08-04 vector メモ


 普通 stl 使い始めて真っ先に使われるのは vector でしょうかね。 配列の延長で使い始めれるし、途中でサイズ増やせるし、 push_back/pop_backでスタックになるし、 何より目新しくてとっつき難いイテレータとかを、 まずは使わずに済ませられるし:-)、で。

 でも、仕様把握しきれずに使っちゃうから、 当初はわけのわからないエラーに翻弄されてしまうこともあったり、 重いコンダラ、もあったしで...。

 で、今はまま、使えているつもりだけれど、ちとアヤフヤも多いんで、ちょっと頭の整理を、と。

 まず、vector の仕様はこちらを参照して、と。

 テスト・ソース&結果はこちらこちら
つーても自分の確認用でしかないんで、わかりにくいし、妥当でもないが...

 面倒なんで、結論だけ。





[前] [次] [戻る]