スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Releaseモードで強制終了するバグの原因が分かった!(その2)

この前、というか昨日のReleaseモードで
強制終了するバグの原因
が分かりました。

vectorのデストラクタは
明示的に呼ぶものではない
ようです。

全体の仕様を把握せずに、
必要なときに、それに該当する(と思われる)関数などをリファレンスから
引っ張ってきてる
という様な事をやってるから駄目なんですね。

とりあえず解決方法。
デストラクタの代わりに、
clear関数を呼び出す事で解決しました。
-----------------------------------
【改善前】 m_WordVec.~vector();
【改善後】 m_WordVec.clear();

-----------------------------------

【補足】
clear関数は、要素は削除されるものの、
内部で確保されているメモリ自体は開放しない
という関数です。

m_WordVec.reserve(100)とやっても、
vector配列の要素数自体は0のままというのと同じような感じですね。

ややこしいですが、要は
要素数 != 内部で確保されているメモリ という事。



とりあえず解決しましたが、(強制終了しなくなりましたが、)
また、どっかでヒープを破壊してたりする様な事が無いとも限らないので、
引き続き、注意しながら開発していきたいです。
スポンサーサイト

テーマ : 日記 - ジャンル : 日記

文字列の変換がややこしいと思った(LPWSTR、CHAR*など)

えーと現在DirectSound8を用いたサウンド再生を行っているのですが、
その際、mmio関数群を用いてWAVEファイルを読み込んでいます。


そしたら、読み込みの際LPWSTRの引数の部分でエラーが出ました。
(一部 変数の宣言部分などを省略してます。)
// Waveファイルオープン関数
bool CSoundFactory::OpenWave( std::string t_FileName) {
// Waveファイルオープン
memset( &mmioInfo, 0, sizeof(MMIOINFO) );
hMmio = mmioOpen( t_FileName, &mmioInfo, MMIO_READ );
return(TRUE);
}


【表示エラー】
1 番目の引数を 'std::string' から 'LPWSTR' に変換できません。


「あぁ、型変換がうまく行っていないんだな。」と思い、
書き直してみたコードがこれ。
// Waveファイルオープン関数
bool CSoundFactory::OpenWave( std::string t_FileName) {
// Waveファイルオープン
char t[100] = {'\0'};
 for(int i = 0, count = t_FileName.size(); i < count; i++)
{
t[i] = t_FileName[i];
}
//LPWSTRへ変換!
LPWSTR tt = (LPWSTR)t;

memset( &mmioInfo, 0, sizeof(MMIOINFO) );
hMmio = mmioOpen( tt, &mmioInfo, MMIO_READ );
return(TRUE);
}


上記の様にすると、コンパイルエラーは発生しなくなりました。
しかし、waveファイルの読み込みに失敗するようになりました。

なぜ・・・!?
と思い、上記のttという変数をしらべてみると、

"轆轤荼薇饂飩"という様に、
意味の分からない文字化けした文字列が格納されてました。

どうも、キャスト(char → LPWSTR)した際に文字化けが発生するようです。
この野郎・・・!!


で、色々と調べてみたところ、
以下の様にしたら、あっさりと読み込みまで成功してしまいました。

bool CSoundFactory::OpenWave( std::string t_FileName) {
// Waveファイルオープン
size_t origsize = t_FileName.size() + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
wchar_t wcstring[newsize];
mbstowcs_s(&convertedChars, wcstring, origsize, t_FileName.c_str(), _TRUNCATE);

memset( &mmioInfo, 0, sizeof(MMIOINFO) );
hMmio = mmioOpen( wcstring, &mmioInfo, MMIO_READ );
return(TRUE);
}

※変数の宣言、エラー処理もろもろ省略してます。

よく分からんが、
mbstowcs_s関数を使えばOKのようです。
こんな関数 聞いたことない・・・!

TCHARとか、LPCTSTRとか、LPTSTRとか 全然なじみが無いし、
変換する関数も 全然知らないものばかりで、

文字列の変換はややこしいなと思いました。
何で こんな状態になってるんだか。

とりあえず、引き続きDirectSound8の実装を続けます!

※今回 参考にしたサイト。↓
【参考URL】さまざまな文字列型間で変換する

テーマ : 日記 - ジャンル : 日記

「どんどん描画が遅くなる欠陥」が判明した。 (DirectXプログラミング)

つい最近ですが、描画オブジェクトを増やすにつれて、
描画がどんどん遅くなる欠陥があることが判明しました。
自分が作ったDirectX描画システムの問題。

遅くなる理由は、単純にLock関数、Unlock関数 を連発する状態になるからです。


プログラムの構造は大体以下のようになってました。
※一部ポインタがNULLだった場合のエラー処理などは省いています。
【参考画像】
explain.png


テクスチャを管理してるクラスがあって、以下のような関数を呼ぶと、
Draw(int t_Handle, int t_PosX, int t_PosY);
Draw(int t_Handle, int t_PosX, int t_PosY, int t_Width, int t_Height);


内部でハンドルに対応するTexture情報を引っ張ってきて、
こんな手順で描画します。
----------------------------------------------------------------
バッファをLock → 座標・サイズ更新 → Unlock
Textureセット → バッファセット → FVFセット → 描画

----------------------------------------------------------------

見てもわかりますが、
for(int i = 0; i < MAP_HEIGHT; i++)
{
for(int j = 0; j < MAP_WIDTH; j++)
{
Draw( m_Handle, (j<<5), (i<<5));
}
}
※MAP_HEIGHT ・・・ 10
 MAP_WIDTH ・・・ 10 とする。

↑こんな事をしようものなら、lock関数、Unlock関数が100回呼ばれます。

自分の買った6年前ぐらいのノートPCでは、
この段階で、動作がスローモーションになってしまいました。

で、思いました。
lock関数、Unlock関数の呼び出し回数を
減らさなければならない
、と。

なので今回、大幅に描画システムを変更いたしました。
どう変更したのかは別記事(プログラミングTIPS)にして記載しようと思います。

ヒント:DrawIndexedPrimitive関数

テーマ : ゲーム - ジャンル : ゲーム

Releaseモードで強制終了するバグの原因が分かった!! (DirectX)

以前、Releaseモードで原因不明の強制終了が発生して、
とんでもなく苦悩したんですが、

原因が分かりました。
実に・・・実に初歩的なことだった・・・!

原因はCOMオブジェクトです。
大まかに"COMオブジェクト"の説明をします。

~COMオブジェクトの特徴~   -------------------------------------------
・独自の生成、解放などに関するメソッド(関数)が用意されているので、
 それを使用する。 中身はブラックボックス。
・COMオブジェクトがコピーされる際、AddRef関数を明示的に呼び出さねばならない。
・デバッグモードでも、COMオブジェクトの解放忘れは検出されない

※以下のメモリリーク検出するコードを記載していても無意味。
プログラムは正常に終了しましたと表示される。

#if _DEBUG
#include
#define new ::new( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF, __FILE__, __LINE__ )
#endif
-----------------------------------------------------------------------

んで、バグの原因について。

端的に言います。
AddRef関数を呼んでませんでした。

ラッパークラスを作っており、
コピーコンストラクタは定義してたんですが、
演算子のオーバーロードを忘れてました。

↓こんな感じ。 コレまるごと記載していなかった。
   void operator =(const CTexture& src){
//バッファのコピー処理
if(src.m_Buf){
src.m_Buf->AddRef();
}
if(this->m_Buf){
this->m_Buf->Release();
}
//テクスチャのコピー処理
if(src.m_Texture){
src.m_Texture->AddRef();
}
if(this->m_Texture){
m_Texture->Release();
m_Texture = NULL;
}

m_Texture = src.m_Texture;
m_Buf = src.m_Buf;
}


↓以下のコピーコンストラクタは定義してたんですけども。
CTexture::CTexture(const CTexture &src)
{
//バッファのコピー処理
if(src.m_Buf){
src.m_Buf->AddRef();
}
if(this->m_Buf){
this->m_Buf->Release();
}
//テクスチャのコピー処理
if(src.m_Texture){
src.m_Texture->AddRef();
}
if(this->m_Texture){
m_Texture->Release();
m_Texture = NULL;
}

m_Texture = src.m_Texture;
m_Buf = src.m_Buf;
}


まぁ、という訳で、
参照カウントが滅茶苦茶になっていたと思われます。

まだ参照してる箇所があるのに、カウントが0になって解放されてたり、
もう参照している箇所は無いのに、カウントが1のままで、解放されない状態だったりして、
壊れてたんだと思います。
(ダングリングポインタ & リソースリーク)


とりあえず修正したところ、
ものの見事にバグが発生しなくなりました。
最高です。

もっとDirectXの基礎的な仕様を把握せにゃならんな、と思いました。

テーマ : 日記 - ジャンル : 日記

かつて戦ったバグ 6

久しぶりにプログラミングでの失敗談を書いてみる。

で、includeの時などに使う くの字型のカッコは、どうやらくっつけて書くと
文字色の指定などを行うタグの一種として判別され表示がおかしくなる為、
両サイドに半角の空白を入れてます。

【例】#include< stdio.h >
('s'の左と、'h'の右)

【バグNO.16】STL (vector)
w-------ww-------ww-------w概略コードw-------ww-------ww-------w

#include< vector >
#include< stdio.h >
int main(void){
vector< int > test;
for(int i = 0; i < 5; i++)
test.push_back(i);

for(int i = 0; i < 5; i++)
printf("test[%d] = %d\n", i, test[i]);
}
w-------ww-------ww-------w概略コードw-------ww-------ww-------w
初めてvectorを使ってみたというプログラムです。
しかし実行しようとすると、
「 'vector' : 定義されていない識別子です。」みたいなエラーがでます。

何でかというと、using宣言を忘れてるからです。

using宣言と言うのは、
スコープ解決演算子(::)を使わずに、アクセス出来るようにするものです。
STLはstd名前空間に含まれる ので、

最初に、
using namespace std;
と記述する必要があります。


#include< vector >
#include< stdio.h >
using namespace std;

int main(void){
vector< int > test;
for(int i = 0; i < 5; i++)
test.push_back(i);

for(int i = 0; i < 5; i++)
printf("test[%d] = %d\n", i, test[i]);
}



もしくは、vectorの部分を、スコープ解決演算子を使って
std::vectorという風にしてもOKです。
(タイプ数が多くなるので、 自分はusing宣言を使ってます。

STLは、単純にヘッダをインクルードするだけで使おうとするとエラーになるので、
初めて使おうとした際に、
using宣言スコープ解決演算子を書き忘れてエラーを出した人は
それなりに居るんじゃないかと思う。
(自分だけかもしれない)


【バグNO.17】STL (string)
w-------ww-------ww-------w概略コードw-------ww-------ww-------w

#include< string >
using namespace std;

int main(void){
string test;
test = "Hello string!!";
printf("%s\n", test);

return(0);
}

【実行結果】
(NULL)

w-------ww-------ww-------w概略コードw-------ww-------ww-------w

今度はSTLのstringを使おうとした例です。
(文字列を簡単に代入できたり、比較できたりします。)

で、文字列を表示させようとしてみると、 実行結果は(NULL)。
「こんにちは」と挨拶したのに(NULL)とは何事か!!
と、憤慨していたところ、

表示する際には
メンバ関数であるc_str()を使わなければならない事が発覚。

なので、printf関数の部分を下記の様に修正したところ、無事表示されました。
printf("%s", test.c_str());

仕様を理解していないまま使おうとすると、大抵ロクでもない事が起こる例でした。


【バグNO.18】 文字列を判別してくれない
w-------ww-------ww-------w概略コードw-------ww-------ww-------w

char str[][25] = { "Frame",
"FrameTransformMatrix",
"Vertex"
"MeshNormals",
};
int flag;
char tstr[100] = "";

while(fscanf(fp, "%s", tstr) != EOF){
flag = -1;
for(int i = 0, count = sizeof(str) / sizeof(str[0]); i < count; i++){
if(strcmp(tstr, str[i]) == 0){
flag = i;
break;
}
}
switch(flag){
case 0: LoadFrame();break;
case 1: LoadMatrix();break;
case 2: LoadVertex();break;
case 3: LoadNormals();break;
default: break;
}
}


w-------ww-------ww-------w概略コードw-------ww-------ww-------w

これは、Xファイルのキーワードがあれば、
そのキーワードに応じた関数を呼び出すプログラムです。

ですが、テキストファイルに"Vertex"とか"MeshNormals"とか書いても、
LoadVertex()や、LoadNormals()の関数は実行されなかったんです。

調べてみたところ、
キーワードが一致した時点で flagに2や3が入り、
for文をbreak;で抜けるようになっているのですが、
"Vertex"と"MeshNormals"を読み込んだ時のflagは-1でした。

つまり、「キーワードと一致していない」と判断されてしまったんです。
もちろん、テキストファイルの方につづりミスなんかはありません。


原因は分かるでしょうか?
(自分はコレで散々な目に遭いました。


では答えです。



コンマが無い。


char型の2次元配列strを見てください。
"Vertex"の後にコンマが無いんです。

正しくはこうです。↓(赤字の部分に注目)

char str[][25] = { "Frame",
"FrameTransformMatrix",
"Vertex",
"MeshNormals",
};



「コンマが無い場合、何が起こっていたか?」というと、
文字列が連結されていたのです。

連続した文字列リテラルは、コンパイル時に連結される んですよ。

例えば下記の場合は
------------------------------------------------------
char test[] = "banana" "mikan";
------------------------------------------------------
testが"bananamikan"になります。くっつくんです。


同じように、2次元配列strも連結され、

char str[][25] = { "Frame",
"FrameTransformMatrix",
"VertexMeshNormals"
};


↑このようになっていたのです。

テキストファイルに、"VertexMeshNormals"なんていうキーワードは当然 存在しない ため、
「キーワードと一致した」とは判断されなかったわけです。

コンマ一つにここまで悩まされるとは・・・。
ちくしょう。

テーマ : 日記 - ジャンル : 日記



上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。