諸事情でJavaScript無しでハンバーガーメニューを実装する必要が出た。つまり、メニュー表示用アイコンを準備し、それがクリックされた時にメニューを表示するという実装。JS無しですってよ奥様。いやいや無理だろ。
ダメ元で調べてみると……いやぁ何事もググってみるもので、CSSのみでマウスクリックを検知し、ページに変化を起こさせるサンプルが見つかった。
続きを読む日 | 月 | 火 | 水 | 木 | 金 | 土 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Linuxで定期的にプログラムを動かす時にはcronと呼ばれる仕組みを使う。また、デスクトップ上に通知バルーンを出すには`notify-send
`というコマンドが使える。これらを組み合わせれば、画面上に定期的に通知を出せる筈だ。
ところが、コレがなかなか上手く行かない。 `crontab -e
` で直接`notify-send
`を指定しても、`notify-send
`を呼び出すシェルスクリプトを指定してもダメ。調べてみると、単純に`notify-send
`を使うだけではダメなようだ。cronで呼び出されるプログラムは、古き良きCUIプログラムである事が前提で云々。
ググると何通りかの回避策が出てくる。が、試した幾つかは上手く行かなかった。もしかしたら同じように悩んでいる人が居るかもしれないし、以下に纏める。
続きを読むPipesの用語がイマイチ分かってない。Moduleの名称や作成画面左下の説明文から推察して使ってる。
既存のsourceに、別のelementを足したい時。
Item BuilderとUnionを使うと別のelementになってしまう。
正解はRenameとRegexを使う。先ずRenameで挿入先sourceの適当なelementをCopy Asし、次にその要素の中身をRegexで入れ替える。
取得したURLに別のqueryを追加したい場合とか。
Regexを使う。適当なタグ(例図では"//TAG//")を決めて、それの追加、置換という順で処理。
うーん、「痒い所に手が届く」ようなModuleが足りない。上に挙げた手もこうやってメモっとかないと1週間後には何故こうしてるのか忘れてそうだ。個人的にはelementの値をtextで取得する手段が欲しい。あとコメントを付ける手段。何かそれを実現するトリックとか在るんだろうか?
あと、Pipesって文字列で表現出来ないからテキストで書きにくいねw
個人的に目から鱗だったのでメモ。
Boost.Testでsetup/teardown的な物を書くにはどうすれば良いのかなーとググって、IT戦記のコメント欄経由でBOOST_FIXTURE_TEST_SUITE()
の存在を知る。
で、ふと該当部分のコードがどうなってるのか気になったので覗いてみた。
以下引用コードは全てBoost 1.45.0のunit_test_suite.hppから。
#define BOOST_FIXTURE_TEST_SUITE( suite_name, F ) \
BOOST_AUTO_TEST_SUITE( suite_name ) \
typedef F BOOST_AUTO_TEST_CASE_FIXTURE; \
/**/
53~56行、BOOST_FIXTURE_TEST_SUITE()
マクロの定義部分。このマクロの第2引数に指定したクラスがfixture実装クラスとなり、コンストラクタがsetup処理、デストラクタがteardown処理になる。利用法は上に挙げたメモの日々(2009-05-26)を参照。
さて、このコードを見ると、fixtureを使わない場合に使用するBOOST_AUTO_TEST_SUITE()
を内部で利用している。BOOST_AUTO_TEST_SUITE()
は44~47行にて定義されており、次のようになっている。
#define BOOST_AUTO_TEST_SUITE( suite_name ) \
namespace suite_name { \
BOOST_AUTO_TU_REGISTRAR( suite_name )( BOOST_STRINGIZE( suite_name ) ); \
/**/
で、実際のテストケースを記述するBOOST_AUTO_TEST_CASE()
マクロは次のように定義されている。114~116行。
#define BOOST_AUTO_TEST_CASE( test_name ) \
BOOST_FIXTURE_TEST_CASE( test_name, BOOST_AUTO_TEST_CASE_FIXTURE )
/**/
BOOST_FIXTURE_TEST_SUITE()
の定義内にてtypedef
されているBOOST_AUTO_TEST_CASE_FIXTURE
が此処で利用されている。
……あれ?でもこれってBOOST_AUTO_TEST_SUITE()
利用時には定義されていない筈だよな?一体どうなってるんだろう?
"BOOST_AUTO_TEST_CASE_FIXTURE
"でコード内を検索すると、以下の記述が見つかる。193~202行。
namespace boost { namespace unit_test { namespace ut_detail {
struct nil_t {};
} // namespace ut_detail
} // unit_test
} // namespace boost
// Intentionally is in global namespace, so that FIXURE_TEST_SUITE can reset it in user code.
typedef ::boost::unit_test::ut_detail::nil_t BOOST_AUTO_TEST_CASE_FIXTURE;
グローバルスコープにて全く同じ名前の型がtypedef
されている。
つまり、BOOST_AUTO_TEST_SUITE()
使用時は、
typedef ::boost::unit_test::ut_detail::nil_t BOOST_AUTO_TEST_CASE_FIXTURE;
namespace test_suite_name {
// 略
BOOST_FIXTURE_TEST_CASE(test_case_name, BOOST_AUTO_TEST_CASE_FIXTURE); // グローバルの物が使われる
// 略
……のように展開され、グローバルスコープに定義されたBOOST_AUTO_TEST_CASE_FIXTURE
が利用される。
が、対してBOOST_FIXTURE_TEST_SUITE()
使用時は、
typedef ::boost::unit_test::ut_detail::nil_t BOOST_AUTO_TEST_CASE_FIXTURE;
namespace test_suite_name {
typedef fixture_class_name BOOST_AUTO_TEST_CASE_FIXTURE;
// 略
BOOST_FIXTURE_TEST_CASE(test_case_name, BOOST_AUTO_TEST_CASE_FIXTURE); // 同じ名前空間内の物が使われる
// 略
……のように展開され、名前空間内のtypedef
に因ってグローバルスコープのBOOST_AUTO_TEST_CASE_FIXTURE
が隠蔽されるようになっているのである。
確かに、このような場合には、より内側の物で外側が隠蔽されると入門書で読んだ記憶があるが、実際に使われているコードを見るのは初めてかも。
目から鱗というか、盲点だったというか。
……を、ずーっと考えてた。
結論、算術演算で何とかなる。
このblogが利用しているblogホスティングサービス、Seesaaブログでは、かなり柔軟なカスタマイズが可能となっている。どれくらいかというと、HTMLを丸ごと書き換える事さえ出来るぐらいだ。
HTMLを書き換える際には、その中にSeesaaブログ独自のタグを埋め込む事になる。『ここにタイトルを入れる』『ここに記事を列挙する』といった指定をするのに。一種のDSLだ。
条件分岐をさせようとした場合、Seesaaブログの独自タグでは次のように書く。
<% if:EXPRESSION %>
<!-- ここにEXPRESSIONが真の時の内容を記述する -->
<% /if %>
例えば、次のように書く。
<% if:page_name eq 'index' %>
<p>このパラグラフは、blogのトップページでのみ出力される</p>
<% /if %>
ところが、EXPRESSIONには、複数の条件をAND/OR下ものを書く事が出来ない。例えば、次のような記述は許可されない(そして、パース処理がエラーを吐く為か、何も表示されない)。
<% if:page_name eq 'index' & page_name eq 'archive' %>
<p>トップページか、過去ログのページで表示させたい。</p>
<p>……が、上手くいかない。</p>
<% /if %>
<% if:page_name eq 'index' and page_name eq 'archive' %>
<p>"and"と書いてもダメ。</p>
<% /if %>
<% if:((page_name eq 'index') and (page_name eq 'archive')) %>
<p>括弧で囲ってもどうにもならない。</p>
<% /if %>
……さてどうしよう?
実は、独自タグ内では算術演算が可能だ。何処まで可能なのか、ハッキリと調べた訳ではないが、加算(+);、減算(-)、乗算(*)が可能なのは確認した。
後は、その特性を利用して、次のように書ける事が分かる。
<!-- 論理和(or) -->
<% if:((page_name eq 'index') + (page_name eq 'archive')) %>
<!-- snip -->
<% /if %>
<!-- 論理積(or) -->
<% if:((page_name eq 'archive') * (archive_page_name ne 'mday')) %>
<!-- snip -->
<% /if %>
否定(not)はちょっと思いつかない。page_name eq 'index'
→page_name ne 'index'
のような、条件を逆にする手法ぐらいだろうか。
先日MSに翻弄された話をポストし、お気に入りのブラウザであるFirefoxで実際に表示させてみて卒倒しそうになった。
URLの部分が改行されない為、tableがとんでもなく見難い状態になっていた。
Firefoxは半角英数記号文字列を途中改行しない仕様だというのは知っていたが、今までは(多少見難くなっても)閲覧にはそれほど影響無いと判断して特に対策は行ってなかった。どうしても気になる人はurl_breaker辺りで対策してね、という事で。
しかし今回のは酷すぎる。何とか対策をしないと。
此処まで閲覧に支障があるのならばサイト側で対策したい。上記拡張のGreaseMonkey版であるurl_breaker_plusを見つけたので、それをそのまま関数化→loadイベントで呼び出せばいいだろう。
url_breakerやurl_breaker_plusの仕様は記号文字毎にwbr
要素を挿入する、というもの。wbr
要素はIEとMozilla系ブラウザが独自に実装している要素で、HTML/XHTML標準規格のものではない。それがチョット気になるが、まぁ、仕方ない。
という訳でurl_breaker_plusのコードをチョイチョイと弄って実装した。
よしよし、ちゃんと改行されるように……
あるぇ?
table
内の文字列が改行されない!?なんでー??
ソースを見てみると、間違いなくwbr
要素が挿入されている。……のに改行されない。ワケが分からん。
url_breakerのページを見てみると、
Ver.0.2.5 (2006/06/17~08/05) ((略)) loadイベントではなくDOMContentLoadedイベントを利用することで、 table内などで折り返しができない問題を若干改善
という記述を発見。……DOMContentLoadedイベント?調べてみると、Collection &\1 Copy - ドキュメントのロード完了に合わせて関数を実行するというページを発見。曰く、「ドキュメントのロードが終わっていて、かつ、イメージのロードが完了する前」に
発生するFirefoxの非標準イベントらしい。試しに変更してみる。……駄目だ。上手くいかない。まぁ、url_breakerのページでも若干改善
としか言っていないし。
念の為url_breakerやurl_breaker_plusをインストールして確認してみたが、やはりtable
要素内はwbr
が挿入されるのに改行されない。まさに若干改善
の若干
外の問題にブチ当たってしまったようだ。
途方に暮れながらwebの大海を彷徨っていると、Wrap!というBookmarkletを公開している人が居た。駄目元で使ってみると……あれ?table
内も改行される……?
よぉし希望が見えてきたぞ。じゃぁコッチのコードを元にしてみよう。やはり関数化してloadやDOMContentLoadedイベントにdocument.addEventListener
する。
……あれ?今度は改行されなくなった……
bookmarkletだと上手くいって、loadやDOMContentLoadedイベントで実行するとtable
内で上手くいかない。こうもワケワカラン状況になるとは……長くなりそうなので、検証用にMSに翻弄された話をHDに保存……あ、間違えて保存したページ開いちゃった。
……………………あるぇ!!?
ローカルに保存したページだと改行が上手くいく。
色々実験。どうやら下に付けているCallbeeのフィードリーダが原因のようだ。外してみたらurl_breakerを元にしたコードもWrap!を元にしたコードも上手くいくし、外部サイト(このblogのページの読込が終わった後?)の読込が絡むと上手くいかないんじゃないかと予想。
という事は、外部サイトのリソースも全て読み込み終わってから処理すると上手くいくという事か。成る程Bookmarkletだと上手く行く訳だ。
少し悩んだが、body
要素の閉じタグ直前にscript
要素でスクリプトを読み込み、実行するようにした。なお、url_breakerを元にしたコードは最後にdocument.style.width = '100%'; focus();
としないと上手くいかなかった。
記号毎にwbr
を文字の前に挿入するか後に挿入するか変える事が出来るWrap!を元に実装。ついでにurl_breakerの指定文字数で強制的に折り返す
を参考に、強制改行(br
要素の挿入)では無くwbr
を挿入するようにしてみた。
……で、出来上がったのがコレ。なにやら謎な分岐があるけど、それについては今度(多分)纏めます。
…………バグにとっつかまって1週間も掛かったのはヒミツだwww
document.style.width = '100%'; focus();
としないと上手くいかなかった折り返し候補位置すべてにWBR要素を挿入してもそれだけではTABLE要素サイズの再計算は行われないので、BODY要素の幅を再設定してドキュメント領域の幅にあわせた折り返しを促しているが原因だろう。
……という訳で、前回の続きである。アレから10日も書かんで何やっとったか!!という不満は山の向こうにポイして頂きたい。
…………イヤほんとすみません。
前回インストールした新しいMSDNライブラリである2006年6月版(以降新ライブラリと呼ぶ)。
"スタート メニュー\プログラム\Microsoft Developer Network"下にショートカットが作成される。VSEE添付のライブラリ(以下旧ライブラリと呼ぶ)とは大違い。
起動すると新ライブラリのホームページが表示される。
…………問題なくインストールされているようだ。
ではIDEから呼び出してみよう。[ヘルプ]→[カテゴリから検索]。よし、ちゃんと呼び出せて……
…………あ、あれぇ?これって旧ライブラリの方じゃん!!
If
とかFor
とか、頭文字の大文字にわざわざShift押すのがメンドクサイ。じゃあって事でWSH標準で使えるもう一つの言語、JScriptでコードを書いてみた。
さて、途中でユーザの入力を受け取る必要が在った訳だが、『JScriptなんてしょせんJavaScriptクローン』と思っていた私は迷うことなく次のようなコードを書いた。
result = prompt('入力してください', '規定値');
……あれー、エラーになるー?
MSDNライブラリやWSHのヘルプを調べてみると、WSH上のJScriptにはprompt()
は用意されていない模様。同等の働きをする関数も無い。
ちょ、JScript使えねー。うーん、やっぱVBScript使うかなぁ。
しかし此処でふと思った。そういえばWSHの標準出力メソッドであるWScript.Echo
はGUI上でのアイコンダブルクリック等での実行の時はダイアログボックスで、CUI上でCSCRIPTコマンドで実行した場合はコマンドプロンプトの標準出力でと場合によってより相応しい方法での出力となる。何で入力はそういう『場合分け』出来るメソッドが無いんだろう?
という訳でサクッと作ってみたのがinput.vbs。どうぞ煮るなり焼くなり御自由に。
var result;
result = aslib_input("プロンプト文字列", "デフォルト値");
プロンプト文字列を表示してキー入力待ち、入力内容が戻り値となる。何も入力しないでEnter押した場合はデフォルト値に設定したものが返る。あー、こういう時って変数がバリアント型な言語だと楽だなぁ。
本当はVBScriptのInputBox()
並みに高機能にしたかった……てか入力待ちの時間を制限するなんてWSHで出来るのか?
WScript.FullName
で実行エンジンがGUI(WSCRIPT使用)かCUI(CSCRIPT使用)かを調べ、それぞれInputBox()
か、WScript.StdOut.Write()
とWScript.StdIn.ReadLine()
のあわせ技かを切り替えている。うーん、GUIかCUIかの判定に『エンジン名を調べる』という方法しか取れなかったのが残念。WSCRIPT.EXE、CSCRIPT.EXE以外の第3のエンジンが在った場合判定しようが無いじゃないか。取り合えず『知らないエンジン名だったらエラー投げる』という仕様にしてある。
使う場合はスクリプト自体をWindows スクリプト ファイルにして<script>要素で読み込む必要が在る。
<?xml version="1.0" ?>
<package>
<job id="testInput">
<script language="VBScript" src="input.vbs"></script>
<script language="JScript">
<![CDATA[
var res = input("prompt", "title", "default");
WScript.Echo(res);
]]>
</script>
</job>
</package>
今まで何でJScript使ってなかったのかというと、VBScriptのCreateObject()
に相当するものがActiveXObjectなんてそのものずばり過ぎる名前だった事。JavaScriptパクリ言語のイメージが有った事が原因。
ところで前者はWScript.CreateObject()
を使う事で解決。……これって、VBScriptのCreateObject()
、JScriptのActiveXObject
とはまた別のものなのだろうか?
C言語のユーザ定義型において、それが『どのタイプのユーザ定義型なのか』という情報はその型のオブジェクトを作る際に必要になる。
つまり、次のようなC/C++のコードがあったとして、
struct Hoge {
int foo;
int bar;
};
このHoge
型のオブジェクトの作り方は少し違う、というオハナシ。
struct Hoge hoge; // C言語の場合; "struct"の部分が必須
Hoge hoge; // C++の場合; "struct"は無くてもいい
でもさ、C言語でオブジェクト宣言時にstruct
とかunion
とかenum
とワザワザ打ちたくない!!C++っぽく出来ないの!?
いろいろPCのトラブルとか暇無いとか忘れてたとかで、やっと記事第1号を書く事になった。いや忘れてたは無いだろ忘れてたは、とか言うツッコミを期待恐れながら、記念すべき第1号記事を書いてみたいと思う。
閑話休題。
さて、第1号記事の内容はこのblog作った当初から決めていて、『BASICのMID$()
をC++で再現する』というものだ。ネタ元はcppllの投稿『[cppll:11798] BASIC の MID$』。
一部のBASIC実装に在ったMID$()
という関数をC++で実装するには?というもの。この関数、ただの関数ではなく代入の左辺に使うことも出来る。
10 STR$ = "hogehoge"
20 RES$ = MID$(STR$, 3, 4) ' STR$の(1バイト文字単位で)3文字目から4文字取得
30 PRINT RES$
40 MID$(STR$, 3, 4) = "PIYO" ' STR$の3文字目から4文字をPIYOに変える
50 PRINT STR$
geho20
行のようなものは普通に書ける(というか普通の関数はこういう仕様)が、40
のようなものを書けるようにする為には……?
早速思いついたので書いてみたのがこれ。
class Mid;
class MidSub {
std::string &str_;
std::string::size_type start_, len_;
public:
MidSub(std::string &to, std::string::size_type start,
std::string::size_type len) :
str_(to), start_(start), len_(len) { };
std::string &operator=(std::string &from) {
// 略
}
operator std::string() {
// 略
}
};
class Mid {
public:
MidSub operator() (std::string &to, std::string::size_type start,
std::string::size_type len) {
return MidSub(to, start - 1, len);
}
} mid;
mid.operator()()
で処理用オブジェクトMidSub
を生成するようになっている。何でこんな回りくどい事になっているかというと、mid(str, 1, 2) = "ほげ"
の場合、普通にやるとmid(str, 1, 2)
(多分operator()
になるだろう)の戻り値に"ほげ"
を代入する、という変な処理となってしまう為だ。
しかし、どうも『Mid
が実質的に何もしていない』『mid
がインスタンス』という点が気持ち悪いように思っていた。
ある日、C++を猛勉強していた某専門学校でのクラスメートYに(ネタとして)見せていると、偶然通りかかったM先生の目に止まる。
天井冴太『~でキモチワルイんですけど、どうにかなりませんかねぇ。』
M先生『なるよ』
天井冴太『(゚д゚)ナンダッテー』
いろいろ話をして、思いついたのがこれ。
class mid {
std::string &str_;
std::string::size_type start_, len_;
public:
mid(std::string &str, std::string::size_type start,
std::string::size_type len = std::string::npos)
: str_(str), start_(start - 1), len_(len) {
// 略
}
std::string &operator=(const std::string &from) { // [mid() = string] ver
// 略
}
operator std::string() const { // [string = mid()] ver
// 略
}
};
mid()
を一時オブジェクト生成のシンタックスを利用するようにした。よくよく考えたら関数のシンタックスと同じなんじゃないか、これって。これによって、midを1つのクラスにまとめる事が出来た。
ひとつ残念だったのは、グローバルなoperator=()
が記述できない点。どうやら言語仕様上そうなっているらしい。operator=(std::string, mid)
を定義出来たらスッキリしたのだが……仕方が無いのでoperator std::string()
を使っている。なので、下手したらキャストする必要が出てきてしまうが、それはまぁ、ご愛嬌という事で。