きままにブログ

プログラミングを主とした私のメモ帳です。寂しいのでコメントください笑

【C++ちゃんは】クイックソートの非再帰版【かわいい】

クイックソートの非再帰

クイックソートは通常、再帰を使って書かれます。再帰呼び出しはスタック領域を使ってしまいますので、呼び出しがあまりにも深くなるとスタックオーバーフローを出してしまいます

そのため、再帰呼び出しを使わないで書いてみました。とはいえ、結局スタックを自分で定義して呼び出すだけなので何もアルゴリズムは変わりません。再帰呼び出しをするどんなアルゴリズムも次のような手順で簡単に非再帰版に変わります。

まずはクイックソート再帰を使って書きます。

#include <algorithm>
#include <vector>
void qsort_normal(std::vector<int>& array, int left, int right) {
	if (left < right) {
		auto i = left;
		auto j = right;
		auto pivot = array[(left + right) / 2];
		
		while (i <= j) {
			while (array[i] < pivot) {
				++i;
			}
			while (array[j] > pivot) {
				--j;
			}
			if (i <= j) {
				std::swap(array[i], array[j]);
				++i;
				--i;
			}
		}
		qsort_normal(array, left, j);
		qsort_normal(array, i, right);
	}
}

では自分でスタックを用意しましょう。とはいえ、スタックはすでに標準ライブラリにありますからそちらを使えばいいでしょう。

置き換えるのは簡単です。

  1. まずは関数の引数の初期値をスタックにプッシュします。
  2. 次に再帰版の関数全体をwhile(スタックが残っている場合) { 処理全体 }とします。
  3. そして、再帰呼び出し部分をそのまま、その引数をスタックにプッシュする処理に置き換えます。

以上です。簡単ですね。それでは実際のコードを見てみましょう。

void qsort_stack(std::vector<int>& array) {
	struct stack_data {
		size_t left;
		size_t right;
	};
	
	std::stack<stack_data> stack;
	stack.push({ 0, array.size() - 1 });
	
	while (!stack.empty()) {
		auto top = stack.top();
		auto left = top.left;
		auto right = top.right;
		stack.pop();
		
		if (left < right) {
			auto i = left;
			auto j = right;
			auto pivot = array[(left + right) / 2];

			while (i <= j) {
				while (array[i] < pivot) {
					++i;
				}
				while (array[j] > pivot) {
					--j;
				}
				if (i <= j) {
					std::swap(array[i], array[j]);
					++i;
					--j;
				}
			}
			stack.push({ left, j });
			stack.push({ i, right });
		}
	}
}

実はこの処理のままだと遅くなります。ここでちょっとだけ最適化を考えます。ポップしてすぐif文を実行してはじかれてしまったらプッシュした意味がなくなります。そこで、pushする前にif文で判定するのです。

また、C++の言語の問題ですが、std::stackは標準で内部のデータ構造にstd::dequeを用います。dequeは両端キューと呼ばれるものですが、先頭への追加を考えるとvectorよりも速いです。ところが、stackは末尾に追加したり末尾から取ってくるデータ構造ですから、そもそも先頭に挿入することがありません。従って、このデータ構造ではなくstd::vectorを用いると速くなります。(なぜdequeなのかは知りません!)

もろもろ改善したコードは次の通りです。

void qsort_stack(std::vector<int>& array) {
	struct stack_data {
		size_t left;
		size_t right;
	};

	std::stack<stack_data, std::vector<stack_data>> stack;
	stack.push({ 0, array.size() - 1 });

	while (!stack.empty()) {
		auto top = stack.top();
		auto left = top.left;
		auto right = top.right;
		stack.pop();
		
		auto i = left;
		auto j = right;
		auto pivot = array[(left + right) / 2];
		while (i <= j) {
			while (array[i] < pivot) {
				++i;
			}
			while (array[j] > pivot) {
				--j;
			}
			if (i <= j) {
				std::swap(array[i], array[j]);
				++i;
				--j;
			}
		}
		
		if (left < j) {
			stack.push({ left, j });
		}
		if (i < right) {
			stack.push({ i, right });
		}
	}
}

次のコードで計測してみました。WindowsTimerクラスは自作の計測クラスで、内部でWin32APIのQueryPerformanceCounterを使っています。

#include <cstdio>
#include <random>
int main() {
	WindowsTimer wt;

	std::vector<int> v;
	std::random_device rd;
	std::mt19937_64 mt(rd());
	std::uniform_int_distribution<unsigned int> uid(0, 10000);

	for (int i = 0; i < 1000000; ++i) {
		v.push_back(uid(mt));
	}
	std::vector<int> v2 = v;

	wt.start();
	qsort_normal(v, 0, v.size() - 1);
	wt.stop();

	printf("normal %f [s]\n", wt.get());

	wt.start();
	qsort_stack(v2);
	wt.stop();
	
	printf("stack %f [s]\n", wt.get());

	system("pause");
	return 0;
}

結果はどちらも同じくらいになりました。めでたしめでたし。、

normal 0.066744 [s]
stack 0.064659 [s]

CSSサンプル記事

目的

CSSレンダリングを確認するための記事です。

見出し

リスト

順不同リスト

  • リスト1
  • リスト2
    • リスト2.1
    • リスト2.2
      • リスト2.2.1
      • リスト2.2.2
  • リスト3

順序リスト

  1. リスト1
  2. リスト2
    1. リスト2.1
    2. リスト2.2
      1. リスト2.2.1
      2. リスト2.2.2
  3. リスト3

定義リスト

定義1
定義内容1
定義2
定義内容2
定義3
定義内容3

ブロック要素

引用

引用1
引用2
引用2

ソースコード

int main() {
    int x = 100;
    printf("x = %d", x);
    return 0;
}

整形

テキストが
     そのまま
         表示されます

インライン要素

em要素
強勢を表します。
strong要素
重要であることを表します。
q要素
引用を表します。
mark要素
読者に注目してほしい部分を表します。
code要素
プログラムのソースコードを表します。
dfn要素
定義語を表します。
ins要素
追記されたことを表します。
del要素
都合によって削除されたことを表します。
s要素
削除されたことを強調するために使われます。
b要素
キーワードや周囲の要素と異なることを明示するために使われます。
i要素
筆者の心情やナレーション等を表します。
samp要素
プログラムの出力を表します。
cite要素
出典を表します。
var要素
変数を表します。

rangeクラスを作り、アダプタを適用していく例

前回の記事の続きですが、作ったアダプタを例えば範囲クラスrangeに適用してみたいと思います。今回は簡単のためイテレータを実装せず、getメンバ関数でrangeを0から順番に回していきます。しかも範囲チェックはしていません。

Step1

まずはアダプタがない状態の範囲クラスrangeを見てみましょう :

class no_adaptor { };

template <>
class range<no_adaptor> {
public:
	range(int i_start, int i_end) : i_start(i_start), i_end(i_end), position(0) {}

	int get() { // イテレータを模す
		int ret =  i_start + position;
		++position;
		return ret;
	}

	int i_start;
	int i_end;
	int position;
};

ご覧のとおりイテレータを内蔵したようなしょうもないクラスですが、getでどんどん取得できるという点が重要です。

Step2

このrangeにアダプタを適用できるように次のメンバ関数を追加しておきます。

template <typename Adaptor>
auto operator>>(Adaptor adaptor) {
	return range<Adaptor>(*this, adaptor);
}

Step3

ではアダプタのあるrangeのクラスを作ります。

template <typename Adaptor>
class range {
public:
	template <typename ItsAdaptor>
	range(range<ItsAdaptor> const& it, Adaptor adaptor) : i_start(it.i_start), i_end(it.i_end), position(it.position), adaptor(adaptor) {}

	int get() { // イテレータを模す
		int ret = i_start + position;
		++position;
		return adaptor(ret); // ここで適用
	}

	Adaptor adaptor;
	int i_start;
	int i_end;
	int position;
};

別のアダプタを持ったrangeをコピーし、新しいアダプタを追加していく例です。

Step4

さらにアダプタを合成できるように次のメソッドを導入します。

template <typename ItsAdaptor>
auto operator>>(ItsAdaptor adaptor) {
	return range<mix_adaptor<Adaptor, ItsAdaptor>>(*this, this->adaptor >> adaptor);
}

実行例

それでは実行例を見てみましょう :

int main() {
	auto r = range<no_adaptor>(1, 10)
		>> make_adaptor([](int n) { return n * n; })
		>> make_adaptor([](int n) { return n + 1; });

	printf("%d\n", r.get()); // 1*1+1 = 2
	printf("%d\n", r.get()); // 2*2+1 = 5
	printf("%d\n", r.get()); // 3*3+1 = 10

	getchar();
	return 0;
}

実装例

class no_adaptor { };

template <typename Adaptor>
class range {
public:
	template <typename ItsAdaptor>
	range(range<ItsAdaptor> const& it, Adaptor adaptor) : i_start(it.i_start), i_end(it.i_end), position(it.position), adaptor(adaptor) {}

	int get() {
		int ret = i_start + position;
		++position;
		return adaptor(ret);
	}

	template <typename ItsAdaptor>
	auto operator>>(ItsAdaptor adaptor) {
		return range<mix_adaptor<Adaptor, ItsAdaptor>>(*this, this->adaptor >> adaptor);
	}

	Adaptor adaptor;
	int i_start;
	int i_end;
	int position;
};

template <>
class range<no_adaptor> {
public:
	range(int i_start, int i_end) : i_start(i_start), i_end(i_end), position(0) {}

	int get() {
		int ret =  i_start + position;
		++position;
		return ret;
	}

	template <typename Adaptor>
	auto operator>>(Adaptor adaptor) {
		return range<Adaptor>(*this, adaptor);
	}

	int i_start;
	int i_end;
	int position;
};

追記

イテレータを実装してみました。begin, endで呼び出せば、次のように使えます((n*n)+1を1~10で回す例) :

auto r = range(1, 10)
	>> make_adaptor([](int n) { return n * n; })
	>> make_adaptor([](int n) { return n + 1; });

for (auto&& it : r) {
	printf("%d\n", it);
}

実装

template <typename Adaptor>
class range_iterator : public std::iterator<std::input_iterator_tag, int> {
public:
	range_iterator(range<Adaptor>* r, int position) : r(r), position(position) {}
	range_iterator(range_iterator const& it) : r(it.r), position(it.position) {}
	auto operator*() const {
		return r->adaptor(position);
	}
	auto operator->() const {
		return r->adaptor(position);
	}
	range_iterator& operator++() {
		++position;
		return *this;
	}
	range_iterator& operator++(int) {
		auto ret = *this;
		++position;
		return ret;
	}
	bool operator==(const range_iterator& it) const {
		return it.position == this->position;
	}
	bool operator!=(const range_iterator& it) const {
		return it.position != this->position;
	}
	range<Adaptor>* r;
	int position;
};

関数の合成を保存し、実行する

関数をどんどん適用できるAdaptorを考えます。

背景

ある関数fがあって、それに対してさらに関数gを適用した関数h = g(f)を考えます。これをC++で表現するというのがこの記事の目的です。ところでどうしてそんなことが必要なのかというと、LINQ for C++を自分で実装しようと考えた時、まずはこのようなアダプタを作らなければならないと思ったからです。これが完成すればあとは適当なrangeに対してイテレータで回すたびに実行すればいいことになります。ただし、takeやreverseとなるとそうはいかなくなりそうなのでとりあえず今回はこのようなものを考えました。

Step1

関数を保存でき、operator()(T)で実行できるようなクラスを作りました。

template <typename Func>
class adaptor {
public:
	adaptor(Func f) : f(f) { }

	template <typename T>
	auto operator()(T t) { return f(t); }
	
	Func f;
};

このクラスによって関数を(オーバーヘッドなしで)保持できます。

Step2

ではこれに対して>>adaptorにて関数の合成を行えるようにしたいと思います。

template <typename ItsFunc>
mix_adaptor<adaptor<Func>, adaptor<ItsFunc>> operator>>(adaptor<ItsFunc> a) {
	return mix_adaptor<adaptor<Func>, adaptor<ItsFunc>>(*this, a);
}

Step3

合成後もインターフェイスとしてoperator()(T)があれば後々問題が生じないため、とりあえずこういうクラスを作りました。したがって合成アダプタクラスも

template <typename Adaptor1, typename Adaptor2>
class mix_adaptor {
public:
	mix_adaptor(Adaptor1 a1, Adaptor2 a2) : a1(a1), a2(a2) { }

	template <typename T>
	auto operator()(T t) { return a2(a1(t)); }

	template <typename ItsFunc>
	mix_adaptor<mix_adaptor, adaptor<ItsFunc>> operator>>(adaptor<ItsFunc> a) {
		return mix_adaptor<mix_adaptor, adaptor<ItsFunc>>(*this, a);
	}

	Adaptor1 a1;
	Adaptor2 a2;
};

と定義しました。

Step4

以上で合成して関数を実行することができます :

template <typename Func>
adaptor<Func> make_adaptor(Func func) {
	return adaptor<Func>(func);
}

int main() {
	auto a = make_adaptor([](int n) { return n + 1; });
	auto b = make_adaptor([](int n) { return n * 2; });
	auto c = make_adaptor([](int n) { return n * n; });
	auto d = a >> b >> c;
	auto e = b >> c >> a;

	printf("%d\n", a(1)); // 2
	printf("%d\n", b(1)); // 2
	printf("%d\n", c(1)); // 1
	printf("%d\n", d(1)); // 16
	printf("%d\n", e(1)); // 5
	getchar();
	return 0;
}

make_adaptorは、ラムダ式型推論をさせるためのヘルパー関数です。このように左から順番に評価されていることがわかります。

scope_exitをmoveできるようにしたmovable_scope_exitを実装

scope_exitでは、その変数を定義したスコープを抜けたときの処理を記述できるように実装しましたが、抜けた時に処理をしないようにする、あるいは処理を延期できるようにすることを目標にmovable_scope_exitを実装しました。ポイントはAnyクラスの実装とほぼそっくりであることです。すなわち内部でポインタとしてtemplateの抽象クラスを保持し、必要なときにインターフェースとしてexecuteを呼び出す、というものです。キャプチャのラムダ式もオーバーヘッドなく使用できます。ただし仮想関数テーブル分および抽象クラス分のオーバーヘッドは避けられません。大したことはないと思いますが……

使用例

{
	movable_scope_exit mse;
	{
		auto se = make_movable_scope_exit(
			[]() {
			printf("out\n");
		});
		mse = std::move(se);
		printf("1\n");
	}
	printf("2\n");
}
printf("3\n");

実行結果は、

1
2
out
3

実装例

class scope_exit_place_holder {
public:
	virtual ~scope_exit_place_holder() { }
	virtual void execute() { }
};

template <typename Func>
class scope_exit_holder : public scope_exit_place_holder {
public:
	scope_exit_holder(Func func) : func(func) { }
	virtual void execute() { func(); }
	Func func;
};

class no_scope_exit { };

class movable_scope_exit {
public:
	template <typename Func>
	movable_scope_exit(Func func) : func(std::make_unique<scope_exit_holder<Func>>(func)) { }
	movable_scope_exit(movable_scope_exit const& it) = delete;
	movable_scope_exit(movable_scope_exit&& it) : func(std::move(it.func)) {}
	movable_scope_exit(no_scope_exit const& it) : func(nullptr) {}
	movable_scope_exit() : func() { }
	movable_scope_exit& operator=(movable_scope_exit const& it) = delete;
	movable_scope_exit& operator=(no_scope_exit const& it) {
		func = nullptr;
		return *this;
	}
	movable_scope_exit& operator=(movable_scope_exit&& it) {
		func = std::move(it.func);
		return *this;
	}

	virtual ~movable_scope_exit() {
		if (func) {
			func->execute();
		}
	}

	std::unique_ptr<scope_exit_place_holder> func;
};

template <typename Func>
static movable_scope_exit make_movable_scope_exit(Func func) {
	return movable_scope_exit(func);
}

SQLite3のテストだよ!

SQLite3

数あるSQLアプリケーションの中でもダントツで早く! 簡単に! 使えるものです。まずはじめにC++(というかC言語)で利用するために適当なデーターベースを作っておきます。test.dbとしてファイルを保存しました。なんでもいいです。

続いてテーブルを作ります。SQLiteについてはSQLite入門を参考にしました。

create table mytable(ID integer, NAME text)

でテーブルを作ります。

ところでSQLiteは文を実行するごとに毎回トランザクションを実行します。これだと動作が遅くなるので、手動でトランザクションを実行させ、他のアプリケーションからのデーターベース操作をブロックします。こちらはBEGINコマンドでトランザクションを開始し、すべての操作が終わったらCOMMITコマンドで更新します。(適当)

API覚書

さて、SQLについては実はよく知らないんですけど() とりあえずSQLite3をコンパイルしてリンクして使ってみました。まずはじめに今回使うAPIを利用順に提示していきます。

sqlite3_open

int sqlite3_open(
  const char *filename,   /* Database filename (UTF-8) */
  sqlite3 **ppDb          /* OUT: SQLite db handle */
);

こいつはfilenameを指定してデーターベースを開くあるいは作成する関数です。だいたいまずはじめに実行します。sqlite3*型へのポインタを渡して書き換えてインスタンスを生成します。まあエラーコードを返すのに戻り値を消費してしまったから引数で書き換えようとしたんですね; C++なら参照で書きたかったかな。std::tupleとかでもいいですね。(余談)

sqlite3_exec

The sqlite3_exec() interface is a convenience wrapper around sqlite3_prepare_v2(), sqlite3_step(), and sqlite3_finalize(), that allows an application to run multiple statements of SQL without having to use a lot of C code.

こいつはSQL文(文字列)を渡してSQLを実際に実行する関数です。この際、sqlite3_openで得られたsqlite3*型のインスタンスを渡す必要があります。その他の引数は次のようになっています。この関数は後に紹介する詳細な関数をすべてセットにしたものです。単にコマンドを実行するだけならこちらを用いると便利です。

int sqlite3_exec(
  sqlite3*,                                  /* An open database */
  const char *sql,                           /* SQL to be evaluated */
  int (*callback)(void*,int,char**,char**),  /* Callback function */
  void *,                                    /* 1st argument to callback */
  char **errmsg                              /* Error msg written here */
);

callbackはコールバック関数で、それに渡す引数が次のvoid*型です。errmsgはエラーメッセージ出力用ですね。callback, parameter, errmsgは省略可能です。その際はNULLを渡します。(nullptrとは書いてなかったです)

If an sqlite3_exec() callback returns non-zero, the sqlite3_exec() routine returns SQLITE_ABORT without invoking the callback again and without running any subsequent SQL statements.

The 2nd argument to the sqlite3_exec() callback function is the number of columns in the result. The 3rd argument to the sqlite3_exec() callback is an array of pointers to strings obtained as if from sqlite3_column_text(), one for each column. If an element of a result row is NULL then the corresponding string pointer for the sqlite3_exec() callback is a NULL pointer. The 4th argument to the sqlite3_exec() callback is an array of pointers to strings where each entry represents the name of corresponding result column as obtained from sqlite3_column_name().

コールバック関数の戻り値はint型ですが、0でない数を返すとsqlite3_exec関数は失敗し、SQLITE_ABORTを返します。つまり常に0を返しておけばよいです。コールバック関数の第1引数は先程のパラメータであり、第2引数が列の数、第3引数はsqlite3_column_textで得られるデータ列、第4引数はsqlite3_column_nameで得られるID列となっています。テキストデータでないもの、例えば整数等は文字列に変換されてデータ列に格納されています。

sqlite3_prepare_v2

sqlite3_execでもSQLは実行できますが、より詳細に実行する場合はこの関数でSQLバイトコードコンパイルし、後々処理していきます。v2はversion2のことでversion1もあるのですが新しいプログラムではこちらが推奨されています。

int sqlite3_prepare_v2(
  sqlite3 *db,            /* Database handle */
  const char *zSql,       /* SQL statement, UTF-8 encoded */
  int nByte,              /* Maximum length of zSql in bytes. */
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
);

dbおよびzSqlは先程と同様です。nByteSQL文(文字列)の最大サイズを指定しますが、負の数を指定することで適当に処理してくれます。

ppStmtはsqlite3_stmt*型へのポインタを指定します。sqlite3_stmt*を宣言し、こいつへのポインタを渡せばOKです。pzTailは文字列zSqlの途中のポインタを指定することができます。すると、そのポインタ以降から実行されます。最初から実行する場合はNULLを指定することができます。

sqlite3_bind_int

SQL文の中に文字列を入れたい場合、sprintfなどで文字列をいじらなくても簡単に設定する機能があります。こちらがこの関数でint型を埋め込むことができます。sqlite3_prepare_v2関数のzSql引数に指定したSQL文のなかにプレイスホルダー「?」を入れることでこれが置き換わります!

int sqlite3_bind_int(sqlite3_stmt*, int, int);

The second argument is the index of the SQL parameter to be set. The leftmost SQL parameter has an index of 1.

第2引数はprepareで作ったstmtに対して、プレースホルダーの番号を1オリジンで指定します。後々取得は0オリジンなので注意が必要です。

The third argument is the value to bind to the parameter.

そして第3引数に数値を入れます。

sqlite3_bind_text

今度はテキストデータをバインドします。

int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));

第3引数以降が変わっています。第3引数はテキストのパラメータです。

If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() is negative, then the length of the string is the number of bytes up to the first zero terminator.

第4引数は文字列の扱いの時と同様に負の数を指定すれば自動的に処理してくれます。

The fifth argument to the BLOB and string binding interfaces is a destructor used to dispose of the BLOB or string after SQLite has finished with it. (中略) If the fifth argument has the value SQLITE_TRANSIENT, then SQLite makes its own private copy of the data immediately, before the sqlite3_bind_*() routine returns.

第5引数は文字列等のリソースの破棄に関する関数ポインタを指定します。今回はSQLITE_TRANSIENTをセットし、SQLが内部でコピーを取り適切に処理させます。

sqlite3_step

After a prepared statement has been prepared using either sqlite3_prepare_v2() or sqlite3_prepare16_v2() or one of the legacy interfaces sqlite3_prepare() or sqlite3_prepare16(), this function must be called one or more times to evaluate the statement.

stmtを実際に実行します。

int sqlite3_step(sqlite3_stmt*);

The details of the behavior of the sqlite3_step() interface depend on whether the statement was prepared using the newer "v2" interface sqlite3_prepare_v2() and sqlite3_prepare16_v2() or the older legacy interface sqlite3_prepare() and sqlite3_prepare16(). The use of the new "v2" interface is recommended for new applications but the legacy interface will continue to be supported.

今回はv2のprepare関数を使っていますが、prepare関数によって挙動が異なるようです。

In the legacy interface, the return value will be either SQLITE_BUSY, SQLITE_DONE, SQLITE_ROW, SQLITE_ERROR, or SQLITE_MISUSE. With the "v2" interface, any of the other result codes or extended result codes might be returned as well.

具体的には戻り値が格段に増えています。

SQLITE_BUSY means that the database engine was unable to acquire the database locks it needs to do its job. If the statement is a COMMIT or occurs outside of an explicit transaction, then you can retry the statement. If the statement is not a COMMIT and occurs within an explicit transaction then you should rollback the transaction before continuing.

SQLITE_BUSYはロックされていたりして実行できない場合に返されます。もしもstmtがCOMMITや自明なトランザクション外で起こった場合、再実行します。もしもそれ以外であった場合はトランザクションを開始する前までロールバックしなければなりません。

SQLITE_DONE means that the statement has finished executing successfully. sqlite3_step() should not be called again on this virtual machine without first calling sqlite3_reset() to reset the virtual machine back to its initial state.

SQLITE_DONEは完了した場合に返されます。これが返った後に再びstep関数を実行してはいけません。もしも実行する場合はreset関数を実行して初期状態に戻ってから実行します。

If the SQL statement being executed returns any data, then SQLITE_ROW is returned each time a new row of data is ready for processing by the caller. The values may be accessed using the column access functions. sqlite3_step() is called again to retrieve the next row of data.

複数の値を返してくる場合、SQLITE_ROWが返ります。これが実行されると行のデータが準備され、行を取得する関数によってデータにアクセスできます。次の行にアクセスするには再びsqlite3_step関数を実行します。

For all versions of SQLite up to and including 3.6.23.1, a call to sqlite3_reset() was required after sqlite3_step() returned anything other than SQLITE_ROW before any subsequent invocation of sqlite3_step().

もしも戻り値がSQLITE_ROWだった場合、次のstep関数を実行する前に次に述べるreset関数を実行しなければなりません。

sqlite3_column_int

もしもstep関数がSQLITE_ROWを返した場合は行の取得となりますのでそれぞれの列から値を取り出してくる必要があります。そのひとつがこの関数でint型として取得できます。

int sqlite3_column_int(sqlite3_stmt*, int iCol);

stmt, 及び列番号iColを指定することで値を取得できます。ただし、列番号は0オリジンであることに注意が必要です。

sqlite3_column_text

const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);

テキストの取得も列番号を指定するだけです。

sqlite3_reset

The sqlite3_reset() function is called to reset a prepared statement object back to its initial state, ready to be re-executed. Any SQL statement variables that had values bound to them using the sqlite3_bind_*() API retain their values. Use sqlite3_clear_bindings() to reset the bindings.

この関数はstmtオブジェクトをリセットし再び実行できる状態に戻します。ただし、sqlite3_bind_*系のAPIはそれぞれ内部データを別で保持しているのでこれらをクリアするにはsqlite3_clear_bindings関数を使わなければなりません。

sqlite3_finalize

The sqlite3_finalize() function is called to delete a prepared statement. If the most recent evaluation of the statement encountered no errors or if the statement is never been evaluated, then sqlite3_finalize() returns SQLITE_OK. If the most recent evaluation of statement S failed, then sqlite3_finalize(S) returns the appropriate error code or extended error code.

stmtを初期化します。直前のSQLが正常に実行されればSQLITE_OKを返し、そうでなければエラーコードを返します。

The application must finalize every prepared statement in order to avoid resource leaks. It is a grievous error for the application to try to use a prepared statement after it has been finalized. Any use of a prepared statement after it has been finalized can result in undefined and undesirable behavior such as segfaults and heap corruption.

リソースリークに繋がるおそれがあるのでSQLite3を利用する際は必ず毎回prepared関数で生成したstmtに対してfinalize関数を実行します。

sqlite3_close

The sqlite3_close() and sqlite3_close_v2() routines are destructors for the sqlite3 object. Calls to sqlite3_close() and sqlite3_close_v2() return SQLITE_OK if the sqlite3 object is successfully destroyed and all associated resources are deallocated.

最後にデータベースを閉じます。関連するリソースは全て解放されます。

実行例

int main() {
	sqlite3* db;

	printf("開く:\n");
	sqlite3_open("test.db", &db);
	
	// 一覧の取得
	{
		sqlite3_exec(db, "select ID, NAME from mytable;", 
					 [](void* p, int n, char** text_list, char** name_list) -> int {
			for (int i = 0; i < n; ++i) {
				printf("%s : %s\t", name_list[i], text_list[i]);
			}
			printf("\n");
			return 0;
		}, nullptr, nullptr);
	}

	printf("トランザクション開始: \n");
	{
		int ret = sqlite3_exec(db, "begin;", nullptr, nullptr, nullptr);
	}

	printf("追加:\n");
	{
		sqlite3_stmt* stmt;
		{
			char str[] = "insert into mytable values(?, ?);";
			int ret = sqlite3_prepare_v2(db, str, -1, &stmt, nullptr);
		}

		int id;
		char name[32];
		printf("(ID,NAME)を入力してね: ");
		scanf_s("%d,%s", &id, name, 32);

		sqlite3_bind_int(stmt, 1, id);
		sqlite3_bind_text(stmt, 2, name, -1, SQLITE_TRANSIENT);

		while (true) {
			int ret = sqlite3_step(stmt);

			if (ret == SQLITE_BUSY) {
				continue;
			}
			else if (ret == SQLITE_DONE) {
				break;
			}
		}

		sqlite3_finalize(stmt);
	}

	// コミット
	printf("コミット: \n");
	{
		int ret = sqlite3_exec(db, "commit;", nullptr, nullptr, nullptr);
	}
	
	printf("検索:\n");
	{
		sqlite3_stmt* stmt;
		{
			char str[] = "select ID, NAME from mytable;";
			sqlite3_prepare_v2(db, str, strlen(str), &stmt, nullptr);
		}

		while (true) {
			int ret = sqlite3_step(stmt);

			if (ret == SQLITE_BUSY) {
				continue;
			}
			else if (ret == SQLITE_DONE) {
				break;
			}
			else if (ret == SQLITE_ROW) {
				int  id = sqlite3_column_int(stmt, 0);
				const unsigned char *name = sqlite3_column_text(stmt, 1);
				printf("ID=%d, NAME=%s\n", id, name);
			}
		}

		// stmt を開放
		sqlite3_finalize(stmt);
	}

	printf("終了:\n");
	sqlite3_close(db);

	getchar();
	getchar();
	return 0;
}

scope_exitの実装

スコープを抜けたら何らかの処理を実行する物を作りました。

使用例

C* p = new C(1, 2);
auto s = mytools::make_scope_exit([&p]() {
	delete p;
});

実装

template <typename Func>
class scope_exit {
public:
	scope_exit(Func func) : func(func) {}
	~scope_exit() {
		func();
	}
private:
	Func func;
};

template <typename Func>
static scope_exit<Func> make_scope_exit(Func func) {
	return scope_exit<Func>(func);
}

とはいえ、単にデストラクタで保持した関数を実行するだけです。テンプレートで保存することでコストの高いstd::function等を利用しなくて済むところがポイントですね。また、make_scope_exit関数を別に用意することでわざわざ型を指定する必要がなくなります。