ラベル C言語 の投稿を表示しています。 すべての投稿を表示
ラベル C言語 の投稿を表示しています。 すべての投稿を表示

2012年1月15日日曜日

gdb で void* 型の変数をデバッグする

C言語で実装されたライブラリやアプリケーションでは、汎用的な型として随所で void* が使用されますが、これをgdbからデバッグすると、そのままでは型情報が無いためタダのポインタとして扱われてしまいます。これではデバッグ時の都合がよろしくないです。
(gdb) print 0xfee65c0
$1 = 267281856
(gdb) print (void *)0xfee65c0
$2 = (void *) 0xfee65c0
こんなとき、この void* が指し示している先の型がわかりきっている場合は、その型でキャストしてやって:
(gdb) print (struct imap_session_state_data *)0xfee65c0
$4 = (struct imap_session_state_data *) 0xfee65c0
(gdb) print $4
$5 = (struct imap_session_state_data *) 0xfee65c0
参照先にアクセスすればきちんと中身が見えます:
(gdb) print * $4
$6 = {
  imap_session = 0xfee6560,
  imap_mailbox = 0xfee6230 "INBOX",
  imap_flags_store = 0xfee6490,
  imap_ssl_callback = 0,
  imap_ssl_cb_data = 0x0
}
(gdb) print $6->imap_session
$7 = (mailimap *) 0xfee6560
(gdb) print * $7
$8 = {
  imap_response = 0xfee6090 "FETCH completed",
  imap_stream = 0xfeecc90,
  imap_progr_rate = 0,
  imap_progr_fun = 0,
  imap_stream_buffer = 0xfee6a00,
  imap_response_buffer = 0xfee6a20,
  imap_state = 3,
  imap_tag = 4,
  imap_connection_info = 0xfee64d0,
  imap_selection_info = 0xfee6030,
  imap_response_info = 0xfee60e0,
  imap_sasl = {
    sasl_conn = 0x0,
    sasl_server_fqdn = 0x0,
    sasl_login = 0x0,
    sasl_auth_name = 0x0,
    sasl_password = 0x0,
    sasl_realm = 0x0,
    sasl_secret = 0x0
  },
  imap_idle_timestamp = 0,
  imap_idle_maxdelay = 1740,
  imap_body_progress_fun = 0,
  imap_items_progress_fun = 0,
  imap_progress_context = 0x0
}
これでデバッグがはかどりました。

2009年10月31日土曜日

libHaru on iPhone: Objective-Cでエラーハンドリング



前回記載できなかったエラーハンドリングについて軽くさわってみました。


■libHaruのエラーハンドリング
http://libharu.org/wiki/Documentation/Error_handlingに詳しく書いてあります。要するに、一番最初のPDFドキュメントを生成するところでエラーハンドラ関数を渡したらあとはエラーが発生するたびにその関数が呼び出されるみたいです。
// まずはヘッダファイルの中で
// ユーザーデータ用の構造体の宣言と、エラーハンドラ関数のプロトタイプ宣言をしておく

typedef struct _PDFService_userData {
HPDF_Doc pdf;
PDFService *service;
NSString *filePath;
} PDFService_userData;

// エラーハンドラ関数のシグネチャは常にこうでなければならない
// 関数名は自由に決めて良いが、引数と返り値は決められている
void PDFService_errorHandler(HPDF_STATUS error_no,
HPDF_STATUS detail_no,
void *user_data);
// 注意点として、エラーハンドラ関数はHPDF_Newより前の行で実装しておかなければならない
// (プロトタイプ宣言しておいてもだめみたいです)
void PDFService_errorHandler(HPDF_STATUS error_no,
HPDF_STATUS detail_no,
void *user_data) {
// ユーザーデータを取得する
// C言語の構造体からでもObjective-Cのクラスが取り出せますよ
PDFService_userData *userData = (PDFService_userData *)user_data;
HPDF_Doc pdf = userData->pdf;
PDFService *service = userData->service;
NSString *filePath = userData->filePath;

// エラーハンドリング後、PDF関連の処理を続行したい場合は
// このHPDF_ResetError関数を呼び出す必要がある。
// これを呼び出さないと後続の処理がうまくいかない。
HPDF_ResetError(pdf);

// 逆にエラーハンドリング後、PDF関連の処理をすべて中断したい場合には
// HPDF_Free関数を使ってpdfオブジェクトを解放するとよい。こうすると、以後の
// HPDF関連の関数はすべて何もしないで終了してくれる。ただし、同じpdfオブジェクトを
// 2回HPDF_Freeしてしまうとエラーになるため、HPDF_HasDocを使って制御すること
HPDF_Free(pdf);
}

// あとはPDFドキュメント生成時に関数とユーザーデータを引数として渡す
int main(int argc, char** args) {
PDFService_userData userData;
HPDF_Doc pdf = HPDF_New(PDFService_defaultErrorHandler, &userData);
userData.pdf = pdf;
userData.filePath = @"/var/tmp/hogehoge.pdf";
// 以下省略
}


■CとObjective-Cをつなげる
C言語的にはこれでよいのですが、Objective-C、ひいてはCocoa Touchフレームワーク的にはこういうエラー処理はdelegateにしてしまったほうが便利です。たとえば、PDFファイルを生成するためのObjective-CクラスPDFServiceに、PDFServiceDelegateを持たせて運用してみましょう。libHaruのエラーハンドラ関数を以下のように作ってみます。
void PDFService_defaultErrorHandler(HPDF_STATUS   error_no,
HPDF_STATUS detail_no,
void *user_data)
{
// ユーザーデータを展開
PDFService_userData *userData = (PDFService_userData *)user_data;
HPDF_Doc pdf = userData->pdf;
PDFService *service = userData->service;
NSString *filePath = userData->filePath;

// 以後のPDF作成処理をすべてストップする
HPDF_Free(pdf);

// Delegate呼び出し
if (service.delegate) {
[service.delegate service:service
didFailedCreatingPDFFile:filePath
errorNo:error_no
detailNo:detail_no];
}
}
あとはこのDelegateを適当なViewControllerなどに準拠させて、
#pragma mark -
#pragma mark delegate method
- (void)service:(PDFService *)service
didFailedCreatingPDFFile:(NSString *)filePath
errorNo:(HPDF_STATUS)errorNo
detailNo:(HPDF_STATUS)detailNo
{
NSString *message = [NSString stringWithFormat:@"Couldn't create a PDF file at %@\n errorNo:0x%04x detalNo:0x%04x",
filePath,
errorNo,
detailNo];
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:@"PDF creation error"
message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] autorelease];
[alert show];
}
とすると、冒頭のイメージのようになります。

2009年5月4日月曜日

Objective-CのnilとNULLの違いって何?

自分用メモ。
nilは「Objective-Cの空のオブジェクト」、NULLは「C言語の空ポインタ」と解釈する。もっとも良い例がNSFileManagerのcreateDirectoryAtPath:withIntermediateDirectories:attributes:error:です。
http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSFileManager_Class/Reference/Reference.html#//apple_ref/occ/instm/NSFileManager/createDirectoryAtPath:withIntermediateDirectories:attributes:error:

リファレンスを読んでみると、attributes:の指定が不要なときはnilを、error:の指定が不要なときはNULLを引数として与えろと明示的に書かれています。これは、attributes:が(NSDictionary *)型=NSDictionaryのオブジェクトを引数として受け取るのに対して、error:は(NSError **)型=NSErrorのオブジェクトのポインタを引数として受け取るためだと考えられます。といっても私はObjective-CもC言語もド素人ですので、ひょっとしたら大嘘かもしれません。違ってたらごめんなさい><


※追記:やっぱり定義そのものが違った><
すたっくおばふろ曰く(http://stackoverflow.com/questions/557582/null-vs-nil-in-objective-c
えらいひと曰く(http://www.libjingu.jp/trans/clocFAQ-j.html#objects-nil


知らないとついつい全部nilもNULLも同じだろと思いnilって指定してしまいそうですね。おそらくnilを指定しても動くとは思いますが・・・Objective-CとC言語の混ざり合いというか関わり合いがこんなところで垣間見れて面白いです。