SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

Visual C++ 2012:stateless-lambdaとSQLiteのぷち拡張

stateless-lambdaのオイシイところ

 stateless-lambdaが関数ポインタに変換できるとナニがオイシイか、ですか? 言うまでもなく「コールバック関数をその場に書き下せる」ことでしょう。たとえばおなじみqsort。qsortはその引数に比較関数のポインタを与えなくてなりません。従来なら:

list-11
int compare(const void* x, const void* y) { 
  return *static_cast<const int*>(x) - *static_cast<const int*>(y); 
}
...
array<int,10> iary = { 9, 8, 7, 6, 5, 0, 1, 2, 3, 4 };
qsort(iary.data(), iary.size(), sizeof(int), &compare);

と書いてたのが:

list-12
array<int,10> iary = { 9, 8, 7, 6, 5, 0, 1, 2, 3, 4 };
qsort(iary.data(), iary.size(), sizeof(int), 
      [](const void* x, const void* y) { 
        return *static_cast<const int*>(x) - *static_cast<const int*>(y); 
      });

のように、比較関数をlambdaでその場に書き下せます。トップレベルウィンドウを列挙するWin-API:EnumWindowsもこんなカンジで使えます:

list-13
// トップレベルウィンドウのタイトルを出力する
EnumWindows(
  [](HWND hWnd, LPARAM lParam) -> BOOL {
    array<char,128> title;
    GetWindowTextA(hWnd, &title[0], title.size());
    if ( title[0] != '\0' ) cout << title.data() << '\t';
    return TRUE;
  }, 0);

 ここに挙げたふたつの例は呼び出し規約が異なる(cdeclとstdcall)けども、vc11はどちらも適切に変換してくれます。

応用例:sqlite3_exec16

 応用例をひとつ紹介、軽量データベースSQLiteです。SQLiteのC-APIにsqlite3_execという「SQL一発実行おちゃらか関数」が提供されています。食わせたSQL文(文字列)にしたがってデータベースを操作してくれますが、「SELECT ...」によるクエリの結果はコールバック関数で捕まえます。こんなカンジ:

list-14
#include <sqlite3.h> // SQLite3
#include <iostream>  // cout, endl
#include <cassert>   // assert

using namespace std;

int print_records(void* ,int n, char** vals, char** cols) {
  for ( int i = 0; i < n; ++i ) {
    cout << cols[i] << "=[" 
         << (vals[i] ? vals[i] : "(null)") << "]\n";
  }
  cout << endl;
  return 0;
}

int main() {
  sqlite3* db;
  char* errmsg;
  int rc;
  
  // データベースのオープン 
  rc = sqlite3_open(":memory:", &db);
  assert( rc == SQLITE_OK );
  rc = sqlite3_exec(db,
                    "CREATE TABLE fruit ( name TEXT, price INTEGER);"
                    "INSERT INTO fruit VALUES ( 'apple' ,  100 );"
                    "INSERT INTO fruit VALUES ( 'banana',  120 );"
                    "INSERT INTO fruit VALUES ( 'cherry',  140 );"
                    "INSERT INTO fruit VALUES ( 'durian', NULL );",
                    NULL, 0, NULL);
  assert( rc == SQLITE_OK );

  // "SELECT..." の結果はコールバックで捕まえる
  rc = sqlite3_exec(db,
                    "SELECT * FROM fruit WHERE name > 'c';",
                    &print_records, &cout, &errmsg);
  if ( rc != SQLITE_OK ) {
    cout << "SQLite err : " << errmsg << endl;
    sqlite3_free(errmsg);
  }
  sqlite3_close(db);
}

 もちろんこいつもlambda使って:

list-15
  ...
  rc = sqlite3_exec(db,
                    "SELECT * FROM fruit WHERE name > 'c';",
                    [](void* ,int n, char** vals, char** cols) {
                      for ( int i = 0; i < n; ++i ) {
                      cout << cols[i] << "=[" 
                           << (vals[i] ? vals[i] : "(null)") << "]\n";
                      }
                      cout << endl;
                      return 0;
                    },
                    nullptr, &errmsg);
  ...

 お気軽にデータベースが扱えるお気に入りのAPIなんですが、気に入らないのはsqlite3_execが扱える文字コードがUTF-8だけで、Unicode(wchar_t)をサポートしてくれていません。それじゃってんでこしらえてみました。

 ヘッダsqlite3_ex.hにUnicode版sqlite3_exec16を用意します。C++用インタフェースには引数の省略を許し、実行エラー時に例外を投げるsqlite3_exec_ex/sqlite3_exec16_exをオマケに付けちゃいます。

list-16
#ifndef SQLITE3_EX_H_
#define SQLITE3_EX_H_

#include <sqlite3.h>

#ifdef __cplusplus
#include <stdexcept> // runtime_error

extern "C" {
#endif

 int sqlite3_exec16(
                 sqlite3* db, 
                 const wchar_t* sql, 
                 int (*callback)(void*,int,wchar_t**,wchar_t**), 
                 void* arg, 
                 char** errmsg
               );
#ifdef __cplusplus
}

class sqlite_error : public std::runtime_error {
  int code_;
public:
  sqlite_error(int c, const std::string& msg) 
    : std::runtime_error(msg), code_(c) {}
  int code() const { return code_; }
};

int sqlite3_exec16_ex(
      sqlite3* db, 
      const wchar_t* sql, 
      int (*callback)(void*,int,wchar_t**,wchar_t**) =nullptr, 
      void* arg =nullptr
    );

int sqlite3_exec_ex(
      sqlite3* db, 
      const char* sql, 
      int (*callback)(void*,int,char**,char**) =nullptr, 
      void* arg =nullptr
    );
#endif

#endif

次のページ
最後に

この記事は参考になりましたか?

特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/6983 2013/02/21 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー