過去ログ 6
過去ログは下にいくほど新しい記事となっております。
※注意:このサイトにあるプログラムのソースコードや、その他の情報はすべて無保証です。やばいと思ったら実行しないでください。コンパイル、実行などによって生じた損害に対する責任は負いませんので、ご了承ください。
ファイル読み書き
20-5-2009
fedora11 リリースがちょっと延びたみたいですね。
今日のSDL
ファイルの読み書きを扱います。
C言語標準入出力ではファイルの読み書きにfopen(),fclose(),fread(),fwrite(),fscanf(),fprintf(),fgetc(),fgets(),... etc.を使いますが、SDLにもファイル操作用の関数が用意されています。
標準入出力とSDLの対応表を載せておきます。(ただし100%互換があるというわけではありません)
標準入出力 | SDL | 説明 |
---|---|---|
fopen() | SDL_RWFromFile() | ファイルオープン |
fclose() | SDL_RWClose() | ファイルクローズ |
fread() | SDL_RWread() | ファイル読み込み |
fwrite() | SDL_RWwrite() | ファイル書き込み |
ftell() | SDL_RWtell() | 現在地取得 |
fseek() | SDL_RWseek() | ファイルシーク |
なお、標準入出力ではファイルポインタ(FILE*)を使ってファイル操作をしますが、SDLではSDL_RWops構造体を使います。構造体の中身はSDL_rwops.hを確認してください。
また、SDL_RWopsはファイルの読み書き以外にも、いろいろな形のデータの操作に汎用的に使うことができます。
関数の説明
SDL_RWops *SDL_RWFromFile(const char *file, const char *mode);
ファイルオープンです。fopen()とほぼ変わりません。*modeもfopen()とおなじく、「w」だの「r」だの指定します。
また、ファイル名ではなく、ファイルポインタを指定する方法もあります。
SDL_RWops *SDL_RWFromFP(FILE *fp, int autoclose);
この場合、「ファイルを開く」ではなく、「開かれたファイルからSDL_RWops構造体を作成する」という方が適当かもしれません。
autocloseは0以外を指定すると、SDL_RWopsを閉じたときに(SDL_RWClose())自動的にファイルポインタもクローズします。
int (SDLCALL *close)(struct SDL_RWops *context); #define SDL_RWclose(ctx) (ctx)->close(ctx)
SDL_RWopsのクローズをします。SDL_PWops構造体のメンバの関数ポインタcloseを呼び出します。
ctxにはクローズするSDL_RWops構造体のポインタを指定します。
成功時に0、失敗時に-1が返ります。
int (SDLCALL *read)(struct SDL_RWops *context, void *ptr, int size, int maxnum); #define SDL_RWread(ctx, ptr, size, n) (ctx)->read(ctx, ptr, size, n)
SDL_PWops構造体のメンバの関数ポインタreadを呼び出します。
ctxにはSDL_RWops構造体のポインタを、ptrには読み込みバッファ領域のポインタを、sizeには読み込み単位を、nには読み込み数を指定します。(バイト数としてはptr*nバイトの読み込みが行われます。
返り値は実際に読み込んだ数が返ります。失敗したら-1が返ります。
int (SDLCALL *write)(struct SDL_RWops *context, const void *ptr, int size, int num); #define SDL_RWwrite(ctx, ptr, size, n) (ctx)->write(ctx, ptr, size, n)
SDL_PWops構造体のメンバの関数ポインタwriteを呼び出します。
ctxにはSDL_RWops構造体のポインタを、ptrには書き込みバッファ領域のポインタを、sizeには書き込み単位を、nには書き込み数を指定します。(バイト数としてはptr*nバイトの書き込みが行われます。
返り値は実際に書き込んだ数が返ります。失敗したら-1が返ります。
int (SDLCALL *seek)(struct SDL_RWops *context, int offset, int whence); #define SDL_RWtell(ctx) (ctx)->seek(ctx, 0, SEEK_CUR) #define SDL_RWseek(ctx, offset, whence) (ctx)->seek(ctx, offset, whence)
SDL_PWops構造体のメンバの関数ポインタseekを呼び出します。
ctxにはSDL_RWops構造体のポインタを、offsetには基準値(whence)からのオフセットを指定します。
whenceにはRW_SEEK_SET(先頭)、RW_SEEK_CUR(現在地)、RW_SEEK_END(末尾)のいずれかがセットされます。
返り値は先頭からのオフセットが返されます。
使用例1 読み込み
(あらかじめファイルを用意しておきます。ここではtest.txtというファイル名で次の内容を書き込んでおきます。)
123456789
ソースコードは次のようになります。
#include <stdlib.h> #include <SDL/SDL.h> #include <SDL/SDL_ttf.h> int main(){ SDL_Surface *screen; SDL_Surface *ttf_surface; char cbuf[100]; SDL_Rect rect_scr = {0,0,800,600}; SDL_Rect rect_img = {0,0,200,200}; SDL_Color color = {255, 255, 255}; int length; TTF_Font *font; SDL_RWops* rwops; SDL_Init(SDL_INIT_EVERYTHING); /* SDL起動 */ TTF_Init(); SDL_WM_SetCaption("タイトル", "たいとる"); SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE); font = TTF_OpenFont("ipag-mona.ttf", 24); screen = SDL_GetVideoSurface(); rwops = SDL_RWFromFile("test.txt", "r"); memset(cbuf, 0, sizeof(cbuf)); length = SDL_RWread(rwops, cbuf, 1, sizeof(cbuf) - 1); SDL_RWClose(rwops); cbuf[length - 1] = '\0'; ttf_surface = TTF_RenderUTF8_Blended(font, cbuf, color); rect_img.w = ttf_surface->w; rect_img.h = ttf_surface->h; SDL_BlitSurface(ttf_surface, &rect_img, screen, &rect_scr); SDL_FreeSurface(ttf_surface); SDL_Flip(screen); SDL_Delay(3000); TTF_Quit(); SDL_Quit(); /* SDL終了 */ return 0; }
使用例2 書き込み
今度は書き込みを行います。ソースコードは次のようになります。
#include <stdlib.h> #include <SDL/SDL.h> #include <SDL/SDL_ttf.h> int main(){ SDL_Surface *screen; SDL_Surface *ttf_surface; char cbuf[] = "987654321"; SDL_Rect rect_scr = {0,0,800,600}; SDL_Rect rect_img = {0,0,200,200}; SDL_Color color = {255, 255, 255}; int length; TTF_Font *font; SDL_RWops* rwops; SDL_Init(SDL_INIT_EVERYTHING); /* SDL起動 */ TTF_Init(); SDL_WM_SetCaption("タイトル", "たいとる"); SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE); font = TTF_OpenFont("ipag-mona.ttf", 24); screen = SDL_GetVideoSurface(); rwops = SDL_RWFromFile("test.txt", "w"); length = SDL_RWwrite(rwops, cbuf, 1, sizeof(cbuf)); SDL_RWClose(rwops); ttf_surface = TTF_RenderUTF8_Blended(font, cbuf, color); rect_img.w = ttf_surface->w; rect_img.h = ttf_surface->h; SDL_BlitSurface(ttf_surface, &rect_img, screen, &rect_scr); SDL_FreeSurface(ttf_surface); SDL_Flip(screen); SDL_Delay(3000); TTF_Quit(); SDL_Quit(); /* SDL終了 */ return 0; }
※メール、リンクは適当に。
マルチスレッド
24-5-2009
今日のSDL
マルチスレッドを扱います。
マルチスレッドは複数の処理を平行して行いたい場合に使用します。
関数 | 説明 |
---|---|
SDL_CreateThread() | スレッド作成 |
SDL_KillThread() | スレッド強制終了 |
SDL_ThreadID() | 自スレッドIDの取得 |
SDL_GetThreadID() | 指定したスレッドIDの取得 |
SDL_Thread *SDL_CreateThread(int (*fn)(void *), void *data);
スレッド作成です。*fnは関数ポインタです。別スレッドで動かす関数をセットします。関数はint型でvoid*型の引数を持ちます。
*dataは*fnの引数にセットするvoid*型ポインタを指定します。
失敗した場合はNULLが返ります。
void SDL_KillThread(SDL_Thread *thread);
スレッド強制終了です。*threadではSDL_CreateThreadで返されたSDL_Threadのポインタをセットします。
Uint32 SDL_ThreadID(void);
Uint32 SDL_GetThreadID(SDL_Thread *thread);
スレッドのID取得用の関数です。スレッドを大量に作成した時にこのIDで管理できます。
このくらいの内容でサンプルプログラムを載せられたら良かったのですが、あまりいいものができませんでしたので、次回にまわします。
※コメント、トラックバックはできません。メール、リンクは適当に。
たまには癒しも
21-5-2009
(癒し期間終了。癒されたい人は下のリンクをクリック)
iGoogleのGadgetです。魚の数を増やしたり、色を変えたりできますよ。
※メール、リンクは適当に。
スレッドの進行制御
26-5-2009
今日のSDL
それぞれのスレッドが独立して好き勝手にやって、好きな時に終了する場合は、SDL_CreateThread()して放置しておけばいいのですが、スレッドが共通のリソースを読み書きするような場合はスレッドの進行制御が必要になります。
方法としてミューテックス(mutex)、セマフォ(Semaphore)がありますが、今回はミューテックスを扱います。
mutexを使った処理の流れ
mutexの操作には「取得」と「解放」の2種類があります。一方のスレッドでmutexの取得を行った場合、他のスレッドではそのmutexが解放されるまで処理が停止します。
◆ 下の絵のような処理があるとします。
◆ mutexの取得までは平行して処理が進みます。
◆ スレッド1にてmutexの取得が行われました。
◆ スレッド1がmutexを解放するまで、スレッド2はmutexの取得ができません。スレッド2はmutexの解放待ち状態となります。
◆ スレッド1がmutexを解放したら、スレッド2がmutexを取得します。
◆ スレッド2がmutexを持っているため、スレッド1はmutexの取得ができません。スレッド1はmutexの解放待ち状態となります。
◆ スレッド2がmutexを解放したら、スレッド1がmutexを取得します。
◆ スレッド1がmutexを解放します。
SDLのmutexについて
各関数は引数も少なく、単純ですので、簡単に説明します。
SDL_mutex *SDL_CreateMutex(void);
mutexを作成します。
void SDL_DestroyMutex(SDL_mutex *mutex);
mutexを削除します。
int SDL_mutexP(SDL_mutex *mutex);
mutexを取得します。成功時は0、失敗時は-1が返ります。
int SDL_mutexV(SDL_mutex *mutex);
mutexを解放します。成功時は0、失敗時は-1が返ります。
mutexというかマルチスレッドの例
スクリーンサーフェスに同時アクセス(致命的エラーになります)しないようにmutexで制御しています。
#include <SDL/SDL.h> #include <SDL/SDL_ttf.h> #define STEP (40) SDL_Surface *screen; SDL_Color color = {255, 255, 255}; TTF_Font *font; SDL_mutex *mutex; /* テキストをスクリーンバッファに書く */ void puttext(char* buf, int x, int y){ SDL_Rect rect_scr = {0,0,800,600}; SDL_Rect rect_img = {0,0,0,0}; SDL_Surface *ttf_surface; rect_scr.x = x; rect_scr.y = y; ttf_surface = TTF_RenderUTF8_Blended(font, buf, color); rect_img.w = ttf_surface->w; rect_img.h = ttf_surface->h; SDL_BlitSurface(ttf_surface, &rect_img, screen, &rect_scr); SDL_FreeSurface(ttf_surface); } /* サブのスレッド */ int sub(void* arg){ int loop; int y = 0; for(loop = 0; loop < 5; loop ++){ SDL_mutexP(mutex); puttext("あいうえお", 200, y); SDL_Flip(screen); SDL_mutexV(mutex); SDL_Delay(500); y += STEP; } return 0; } int main(){ int loop; int y = 0; SDL_Init(SDL_INIT_EVERYTHING); /* SDL起動 */ TTF_Init(); SDL_WM_SetCaption("タイトル", "たいとる"); SDL_Thread *thread; SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE); font = TTF_OpenFont("ipag-mona.ttf", 24); screen = SDL_GetVideoSurface(); mutex = SDL_CreateMutex(); thread = SDL_CreateThread(sub, NULL); for(loop = 0; loop < 5; loop ++){ SDL_mutexP(mutex); puttext("あいうえお", 0, y); SDL_Flip(screen); SDL_mutexV(mutex); SDL_Delay(1000); y += STEP; } SDL_WaitThread(thread, NULL); SDL_DestroyMutex(mutex); SDL_Delay(1000); TTF_Quit(); SDL_Quit(); /* SDL終了 */ return 0; }
メインスレッドは1秒に1回左側に文字を書く、サブスレッドは0.5秒に1回右側に文字を書く処理を行います。
mutexの操作をしている部分を強調しておきました。
puttext()でグローバルのスクリーンバッファにアクセスしているのに、内部でアクセス制御していない。別途制御が必要な、中途半端な関数になってしまった。反省。
SDL_WaitThread()の説明をしていなかったので、ここに書きます。
void SDL_WaitThread(SDL_Thread *thread, int *status);
スレッド終了まで、処理を停止します。*threadには終了待ちスレッドを、*statusにはスレッドの関数の返り値をセットするためのポインタをセットします。NULLをセットすると無視されます。
処理待ち系関数(SDL_WaitThread()、SDL_mutexP())にはタイムアウトが設定できないので無限待ちとなります。注意してください。
※メール、リンクは適当に。
サイトのメンテナンス
27-5-2009
を、やらなければならないなー、と思いつつ、全然やっていない。
Freecivのページとか訪問者が多いから色々直さなければいけないんだけど。
とか思っているうちにニコニコ動画の方で紹介されていたようです(アクセス解析より判明)。
Freecivを紹介しつつ難易度普通に勝ちたい動画 その2 (ユーザー登録必要)
どうもありがとう。こんな放置ぎみのサイトを紹介してくれて。ってアップロード日時2008年8月ですか。全然気がつかなかった。
これからは心を入れ替えてまじめに更新しますので、応援よろしくね。
さて、スクリーンショットを整理する作業に戻ろうか・・・。
(ここからはいつものSDL講座です)
今日のSDL
semaphoreです。semaphoreもmutexと似たようなものです。mutexと違うのはsemaphoreは取得できる数を自由に設定できる点です。
mutexの場合はどれかのスレッドが取得したら、他のスレッドはmutex取得できず解放待ちになるのに対し、semaphoreはあらかじめ決めておいた数だけ取得できます。
例えば、セマフォの初期値を2としておくと、1番目のスレッドが取得した後、2番目のスレッドも取得できます。3番目のスレッドは、1番目、2番目のいずれかのスレッドのsemaphore解放待ちとなります。
SDLのsemaphoreについて
こちらも各関数は引数も少なく、単純ですので、簡単に説明します。
SDLではsemaphoreの方はタイムアウトが指定できたり、mutexより高性能ですね。
SDL_sem *SDL_CreateSemaphore(Uint32 initial_value);
semaphoreを作成します。initial_valueはsemaphoreの初期値です。
void SDL_DestroySemaphore(SDL_sem *sem);
semaphoreを削除します。
int SDL_SemWait(SDL_sem *sem);
semaphoreを取得します。semaphoreの数が0の場合は解放待ちになります。成功時には0を、失敗時には-1を返します。
int SDL_SemTryWait(SDL_sem *sem);
semaphoreを取得します。semaphoreの数が0の場合はSDL_MUTEX_TIMEDOUTを返します。成功時には0を、エラー時には-1を返します。
int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout);
semaphoreを取得します。timeoutはタイムアウト(ミリ秒)です。タイムアウト時にはSDL_MUTEX_TIMEDOUTを返します。成功時には0を、エラー時には-1を返します。
int SDL_SemPost(SDL_sem *sem);
semaphoreを解放します。
Uint32 SDL_SemValue(SDL_sem *sem);
現在のsemaphoreの数を取得します。
サンプル
前回にも増していいかげん・・・。使い方としては非常にしょうもない・・・。
#include <SDL/SDL.h> #include <SDL/SDL_ttf.h> #define STEP (40) SDL_Surface *screen; SDL_Color color = {255, 255, 255}; TTF_Font *font; SDL_mutex *mutex; SDL_sem *semaphore; /* テキストをスクリーンバッファに書く */ void puttext(char* buf, int x, int y){ SDL_Rect rect_scr = {0,0,800,600}; SDL_Rect rect_img = {0,0,0,0}; SDL_Surface *ttf_surface; rect_scr.x = x; rect_scr.y = y; ttf_surface = TTF_RenderUTF8_Blended(font, buf, color); rect_img.w = ttf_surface->w; rect_img.h = ttf_surface->h; SDL_BlitSurface(ttf_surface, &rect_img, screen, &rect_scr); SDL_FreeSurface(ttf_surface); } /* サブのスレッド */ int sub(void* arg){ int loop; int y = 0; int x = *(int*)arg; SDL_SemWait(semaphore); for(loop = 0; loop < 5; loop ++){ SDL_mutexP(mutex); puttext("☆", x, y); SDL_Flip(screen); SDL_mutexV(mutex); SDL_Delay(100); y += STEP; } SDL_SemPost(semaphore); return 0; } int main(){ int loop; int y = 0; int x = 0; SDL_Init(SDL_INIT_EVERYTHING); /* SDL起動 */ TTF_Init(); SDL_WM_SetCaption("タイトル", "たいとる""; SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE); font = TTF_OpenFont("ipag-mona.ttf", 24); screen = SDL_GetVideoSurface(); mutex = SDL_CreateMutex(); semaphore = SDL_CreateSemaphore(2); for(loop = 0; loop < 5; loop ++){ x = loop * STEP; SDL_CreateThread(sub, &x); } SDL_Delay(5000); SDL_DestroyMutex(mutex); SDL_DestroySemaphore(semaphore); TTF_Quit(); SDL_Quit(); /* SDL終了 */ return 0; }
サンプルプログラムでは5つのスレッドを作成しますがセマフォの制御により、同時に動くのは2つのスレッドとなります。
そのため、最初に2つのスレッドが動いている間、他のスレッドはSDL_SemWait()で待機することになり、解放されたら順次残りのスレッドが処理を行う、という流れになります。
※メール、リンクは適当に。
Copyright (c) 2008, 2009 greencap