Hatena::ブログ(Diary)

GANAwareはてな版 RSSフィード

2013-10-08

C++11 の正規表現ライブラリの曖昧さ

libstdc++ の regex が実装されたそうです。

そこで、仕様曖昧な点(2点)を調査して纏めました。

GitHub Pages 機能を使ったことが無かったので、試しに使ってみようということで上記のところで公開してあります

gh-pages ブランチの中で symlink を作っても、GitHub Pages で公開されたページでは symlink は辿ってくれないようでした。そのため、ちょっとだけ修正が必要でした。

2013-07-02

正規表現で [a-[:alpha:]] の意味って?

C++11仕様に対する疑問

C++11正規表現 (std::regex_constants::ECMAScript 時) の仕様ECMA-262 の文法を次のように微妙に変更したものとなっています

ClassAtom ::
	-
	ClassAtomNoDash
	ClassAtomExClass
	ClassAtomCollatingElement
	ClassAtomEquivalence
ClassAtomExClass ::
	[: ClassName :]
ClassAtomCollatingElement ::
	[. ClassName .]
ClassAtomEquivalence ::
	[= ClassName =]
ClassName ::
	ClassNameCharacter
	ClassNameCharacter ClassName
ClassNameCharacter ::
	SourceCharacter but not one of "." "=" ":"
(ドラフト n3337.pdf より)

そして ECMA-262 の正規表現の文字クラス周りの文法はだいたい以下のような感じになっています

CharacterClass ::
	[ [lookahead ∉ {^}] ClassRanges ]
	[ ^ ClassRanges ]
ClassRanges ::
	[empty]
	NonemptyClassRanges
NonemptyClassRanges ::
	ClassAtom
	ClassAtom NonemptyClassRangesNoDash
	ClassAtom - ClassAtom ClassRanges
NonemptyClassRangesNoDash ::
	ClassAtom
	ClassAtomNoDash NonemptyClassRangesNoDash
	ClassAtomNoDash - ClassAtom ClassRanges
ClassAtom :: (ここは C++11仕様で変更される)
	-
	ClassAtomNoDash
ClassAtomNoDash ::
	SourceCharacter but not one of \ or ] or -
	\ ClassEscape
ClassEscape ::
	DecimalEscape
	b
	CharacterEscape
	CharacterClassEscape
(Standard ECMA-262 より)

ここで、強調した部分、つまり以下の部分

ClassAtom ::
	ClassAtomExClass
NonemptyClassRanges ::
	ClassAtom - ClassAtom ClassRanges
NonemptyClassRangesNoDash ::
	ClassAtomNoDash - ClassAtom ClassRanges

を素直に解釈すると、例えば次のような正規表現は文法上正しいということになります

[a-[:alpha:]]
[[:alpha:]-a]
[[:alpha:]-[:alpha:]]

文法上は正しいといっても、何を表現しているのか意味がよくわかりません。

現実の実装では?

例えば、Boost C++ Libraries ではテストケース (test_sets.cpp) 内で、以下のようなテストをしており、

   TEST_INVALID_REGEX("[a-[:alpha:]]", boost::regex::extended);

問題となる正規表現記述できないことを確かめています

また、OS X Mountain Lionclang-425.0.28 では、

#include <regex>
#include <iostream>

int main() {
	std::locale::global(std::locale("C"));
	std::regex re("[a-[:alpha:]]");
	std::cout << std::boolalpha << regex_match("a", re) << std::endl;
	std::cout << std::boolalpha << regex_match("b", re) << std::endl;
	return 0;
}

上記のソースを -std=c++11 -stdlib=libc++ でコンパイルして実行すると

true
false

が出力されます。なにが起こったのかよくわかりません。

また、Visual Studio 2012 Express では上記のソースを実行すると std::regexコンストラクタ内で std::regex_error 例外が .code() == std::regex_constants::error_range で throw されます

まとめ

というわけで、この問題は C++11仕様の間違いとして解釈するのが良いと思います

2010-01-29

allocatorが異なるstring

typedef std::basic_string<wchar_t, std::char_traits<wchar_t>, 
                          my_allocator<wchar_t> > my_wstring;
std::wstring str1;
my_wstring str2;
str1 == str2; // error
str1 < str2; // error
str1 = str2; // error

うーん、不便。allocator が異なる場合でもこれらの関数は問題なく実装できると思うのですけれど。

2009-12-08

C++0x 難しい。

和訳:Rvalue References: C++0x Features in VC10, Part 2 - ntnekの日記

この記事のおかげで、右辺値参照とムーブセマンティクスを理解できたような気がします

2009-07-31

int** ⇒ const int**

404 Not Found

int ** を const int ** へ代入できないのはなぜか? という話。

int* pi;
const int** ppci = &pi; // ここでコンパイルエラー
const int ci = 0;
*ppci = &ci;
*pi = 3;
// ↑エラーが起こらなければ、
// const なはずの ci の値を書き換えできてしまう

うっかり似たようなコードを書いてコンパイラに怒られた経験が何度もあるので、自分的にはポインタのポインタはなんとなく苦手です。


d.y.d.

B ** を A** へ代入できないのも、同様の理由だから、という話。

class A { };
class B : public A { public: void method_of_b(); };

B* pb;
A** ppa = &pb; // ここでコンパイルエラー
A a();
*ppa = &a;
(*pb).method_of_b();
// ↑エラーが起こらなければ、
// A のオブジェクト a に対して
// B のメソッドを呼び出すことができてしまう

こちらの場合は、

B b;
B *pb = &b;
A *pa = pb;
assert(reinterpret_cast<long>(pa) == reinterpret_cast<long>(pb));

最後の assert は A と B の定義の仕方によっては、成り立たないことがしばしばあるということからもわかります