2011年07月28日

C++でchar[][]に対するalgorithmを実装する

この記事は以下の続きです

  1. C/C++で多次元配列を引数に取るには
  2. C/C++で多次元配列を戻り値にするには

char[][]に対するalgorithmが欲しい

C++はその標準ライブラリの一部であるSTLにて、ジェネリックプログラミングで各種処理を抽象化した関数群(algorithm)が用意されている。sortとかcountとか、for eachまで。
そして、C++(そのベースとなったC言語由来だが)で文字列は文字の配列(char[])として表現する都合上、『文字列の配列』は文字の二次元配列(char[][])として表現する事になる(std::string[]については此処では触れない)。

……じゃあ、『文字列の配列』用に特殊化したalgorithmが欲しくなるよね?

char[][]に対するstd::find()を実装する

……という訳で、char[][]用のstd::find()を実装してみた。同じようにして、wchar_t[][]用や、他のalgorithmも実装出来る筈。

#include <algorithm>
#include <functional>
#include <cstddef>
#include <cstring>

#define NS aslib
// #define NS std
// ↑ユーザー定義型用の完全特殊化以外をstd名前空間に記述するのは本当は×

namespace {
	template<typename Itr>
	Itr find_detail_for_char2darray(Itr begin, Itr end, const char *target) {
		return std::find_if(begin, end, 
			std::not1(std::bind2nd(std::ptr_fun(std::strcmp), target))
		);
	}
}

namespace NS {
	template<std::size_t N>
	const char (*find(const char (*begin)[N], const char (*end)[N], const char *target))[N] {
		return ::find_detail_for_char2darray(begin, end, target);
	}

#if NS==std
	// std下に置く場合は、以下の特殊化の定義も必要となる

	template<std::size_t N>
	char (*find(char (*begin)[N], char (*end)[N], const char *target))[N] {
		return find_detail_for_char2darray(begin, end, target);
	}

	template<std::size_t N>
	const char (*find(const char (*begin)[N], const char (*end)[N], char *target))[N] {
		return find_detail_for_char2darray(begin, end, target);
	}

	template<std::size_t N>
	char (*find(char (*begin)[N], char (*end)[N], char *target))[N] {
		return find_detail_for_char2darray(begin, end, target);
	}
#endif
}

ソースはこちら。おまけで確認用のmain()も記述している。Ideone.comにもUPしているので、そちらの方が手軽かも知れない。

ソース中でも言及しているが、本来、std名前空間下には、ユーザー定義型用に完全特殊化したもの以外は追加してはいけない事になっている(らしい)。
char[][]と同じ名前空間(上記の場合はaslib)下に、std::find()を呼び出すだけの汎用版を用意し、同じインタフェイスで使えるようにするのがスマートかも知れない。

ラベル:C++
posted by 天井冴太 at 00:38| Comment(0) | TrackBack(0) | Other | 更新情報をチェックする

2011年07月22日

C/C++で多次元配列を戻り値にするには

前回はC/C++で多次元配列を引数にとる方法を記した。引数があるなら戻り値はどうだ?というのか今回の趣旨。正確には『多次元配列を指すポインタ』な訳であるが。

結論から言うと、以下のようになる。

/* int[x][3]を指すポインタを返す関数 */
int (*function(int))[3];
int (*function(int))[3] {
	/* ... snip ... */
}

これは『配列を指すポインタ』の変数宣言とよく似ている。
また、前回挙げた引数の書き方(の1つ)ともよく似ている。

/* int[x][3]を指すポインタ */
int (*pointer)[3];
/* int[x][3]を指すポインタを引数とする関数 */
int function(int (*arg)[3]);

ただし、引数の場合と異なり、以下のような書き方は出来ない。

/* int[x][2](を指すポインタ)を返す関数……のつもり */
int functionX(int)[][2];	/* だがエラーだ */

引数の場合と同様、C++であればtemplateを用いて、一番右の次元の要素数を限定せずに書く事が出来る。

template<std::size_t N> int (*function(void))[N];
int (*ret1)[1] = function<1>();
int (*ret2)[2] = function<2>();

さらに次回に続く。

参考

ラベル:C C++
posted by 天井冴太 at 22:10| Comment(0) | TrackBack(1) | Study | 更新情報をチェックする

C/C++で多次元配列を引数に取るには

C言語学習時、確かに勉強した記憶があるんだが書き方忘れた。で、詰まった。
備忘録代わりにメモメモ。

int array[2][3];

なんて配列があって、これを引数に取る関数は、以下のようになる。

/* [x][3]な二次元配列を取る関数 */
int function(int arg[][3]);
int function(int (*arg)[3]);	// これでもおk

n次元配列の、右から数えてn-1次元目までは要素数を書かなければならない。
つまり、

/* [x][4][5]な三次元配列を取る関数 */
int function3(int arg[][4][5]);
/* [x][6][7][8]な三次元配列を取る関数 */
int function4(int arg[][6][7][8]);

ただし、

/* 1次元目も2次元目も何が来ても問題なく処理出来る関数……のつもり */
int functionX(int arg[][]);

みたいな真似は不可。
まぁ、C/C++の配列の仕様(添字が範囲外の値かどうかチェックしない)を考えると、この制限も納得なんだけど。

C++だとtemplate使えば上で挙げたような真似は一応出来る。

template<std::size_t N> int function(int arg[][N]);
int a[2][3], b[4][5];	// aとbの添字の数値が異なる点に注目
function(a);
function(b);

まぁこれで何が嬉しいかというと、例えばchar[][]から任意文字列を探すstd::find()の特殊化が出来るよね、とか。

次回に微妙に続く。

ラベル:C C++
posted by 天井冴太 at 01:44| Comment(0) | TrackBack(2) | Study | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は180日以上新しい記事の投稿がないブログに表示されております。