「テンプレート」と一致するもの

MT5 移行で 2 度はまった

ようやく MT5 (正確には MTOS 5.04)に移行した。MT4 からの upgrade なので、テンプレートとかプラグインとかも MT5 対応をうたったものに移行した。図らずも、この移行作業で年を越してしまった。

で、移行後の記事を何とは無しに見ていたら、pre 要素内の改行が無視されていることに気づいた。以下、解決に至るまでの様子。

pre 要素って元々そういう物だったっけ ?
→ No. pre 要素内の改行コードは表示にもそのまま反映されるはず。
じゃあ改行を無視するようなスタイル指定がされている ?
→ No. テンプレート(mt.Vicuna Simple)の CSS をざっと見たけどそんな指定はない。
ところで特定要素のスタイル適用状況を調べるのに Safari 便利すなぁ。(単に Firebug とかをまだインストールしてなかっただけ)
そもそも pre 要素内に改行コードはちゃんと入ってる ?
→ No! つーかよく見たら記事本文が全部 1 行に連結されちゃってるし。MT5 が記事を生成するときに改行コードを削除してるということかー

ということでテンプレートを見てみた。

<MTIf tag="EntryBody" strip_linefeeds="1" trim="1">
                <div class="textBody">
<$MTEntryBody$>
                </div>
</MTIf>

なんだ、strip_linefeeds の値を 0 にすれば良いだけか。ついでに trim0 にするか。で再構築、と。

……
解せぬ。何故変わらぬ。

あれ、そもそもこの 2 つのモディファイア自体要らねーんじゃね ? オリジナル(MT4 用)にも無いし。
ということで試しに削除したら、うまくいった。
なんでやねん…
なんでやねん……

GX-D90(B) ほか

ヘッドフォン使うと耳が痛くなる上に髪型が変になるから,風呂上がりとかは特に使う気が無くなる.せっかく高い金払って買ったのにね.言うほど髪型を気にしているわけではないけれどね.

ということで,昨日 Amazon で GX-D90(B) をポチった.ONKYO にしたのは,サウンドカードが SE-90PCI だから.GX-100HD(B) とかにしなかったのは,コストパフォーマンス的に良さげ & グレード的に SE-90PCI と釣り合うと思ったから.お急ぎ便にしてみたら本当に当日に届いた.さすが関東.

部屋を片付けて,MDR-DS7000 のプロセッサーと光デジタル接続して,Amarok 1.4 で MP3 を再生してみた.エージングしていない (面倒だし効果のほどもよく分からんのでする気は無い) 状態でも低音が良い感じ.今まで出力の小さいスピーカー / ヘッドフォンしか使ってなかったからそう感じるのかも知れない.

今回,ついでに以下も購入した.

まだ 1 冊目しか目を通していない.GW-USMicroN は DS 専用 AP にするつもりで買ったんだけど… Linux ドライバ (RT2870) のソースに目を通してみたところ Master モードには非対応のようなので,計画失敗.事前調査もせずに買っちゃうから…

ようやく移行

テンプレートを作る気力がほぼ皆無になっちまったので,Vicuna を利用することにした.無彩色なら何だって良いよもう.

とりあえず記事のインポートだけ済ませた.あとは過去の記事とか画像とかへのリンクを修正しないとね….まぁそれくらいだったら今後ぼちぼちやっていけば良いか.

prefixed_ostream

目的

次のような出力を行いたい.


#include <iostream>
#include <fstream>
#include "prefixed_ostream.h"

int main(void)
{
    prefixed_ostream  warning(std::cerr, "WARNING: ");  // 警告出力用フィルタ
    prefixed_ostream  error(std::cerr, "ERROR: ");      // エラー出力用フィルタ

    warning << "This is a warning message." << std::endl;
    error << "This is a long error message from here...\n"
          << "(snip)\n"
          << "to here." << std::endl;

    std::ofstream     ofs("output.txt");    // 例外処理は省略
    prefixed_ostream  comment(ofs, "# ");   // コメント出力用フィルタ

    ofs << "This is a usual line." << std::endl;
    comment << "This is a comment line." << std::endl;
    ofs << "This is a usual line again." << std::endl;

    return 0;
}

$ ./a.out
WARNING: This is a warning message.
ERROR: This is a long error message from here...
ERROR: (snip)
ERROR: to here.
$ cat output.txt
This is a usual line.
# This is a comment line.
This is a usual line again.
$ 

つまり,指定した文字列を各行の先頭に自動的に付記して出力できるようにしたい.

実現方法

このページで提案されている 「フィルタ」 なる概念を利用する.

実装例

バッファが溢れた場合の処理が不適切なためにセグフォが発生し得る事が判明したので,関数 write_with_prefix() の処理を修正 (及びメンバ is_line_head_ を追加).

極力 1 行単位で出力するように修正.


#if !defined PREFIXED_OSTREAM_H_INCLUDED_
#define PREFIXED_OSTREAM_H_INCLUDED_

#include <streambuf>

/// プレフィックスを付記する出力ストリームクラステンプレート
template <typename CharT, typename Traits = std::char_traits<CharT> >
class basic_prefixed_ostream : public std::basic_ostream<CharT, Traits>
{
public:
    /**
     * @brief コンストラクタ
     * @param [in,out] os          対象とする出力ストリーム
     * @param [in]     prefix      プレフィックス
     * @param [in]     buffer_size バッファのサイズ
     */
    basic_prefixed_ostream(
        std::ostream& os,
        const char* prefix = "# ", std::size_t buffer_size = 1024)
        : std::basic_ostream<CharT, Traits>(
            new streambuf<CharT, Traits>(os, prefix, buffer_size)) {}

    /// デストラクタ
    virtual ~basic_prefixed_ostream()
    {
        this->flush();
        delete this->rdbuf();
    }

protected:
    /// basic_prefixed_ostream 専用のストリームバッファクラステンプレート
    template <class CharT2, class Traits2 = std::char_traits<CharT2> >
    class streambuf : public std::basic_streambuf<CharT2, Traits2>
    {
        typedef CharT2                                      char_type;
        typedef Traits2                                     traits_type;
        typedef typename traits_type::int_type              int_type;
        typedef std::basic_ostream<char_type, traits_type>  ostream_type;

        ostream_type*     os_;           ///< 対象とする出力ストリーム
        const char_type*  prefix_;       ///< プレフィックス
        std::size_t       prefix_size_;  ///< prefix_ の文字列長
        char_type*        buffer_;       ///< バッファ
        std::size_t       buffer_size_;  ///< バッファのサイズ
        char_type*        head_;         ///< 書き出すデータの先頭位置
        bool              is_line_head_; ///< 真の時 prefix_ を出力する

        /// バッファをクリアする
        inline void clear_buffer(void)
        {
            traits_type::assign(buffer_, 0, buffer_size_);
            this->setp(buffer_, buffer_ + buffer_size_);
            head_ = buffer_;
        }

        /**
         * @brief プレフィックスを付記して出力する
         * @param [in] force 真の時バッファの中身を強制的に出力する
         */
        void write_with_prefix(bool force)
        {
            // 改行文字が現れる度に 1 行ずつ出力する
            for (char_type* pos = head_; pos < this->pptr(); pos++)
                if (*pos == '\n')
                {
                    if (is_line_head_)
                        os_->write(prefix_, prefix_size_);
                    os_->write(head_, pos + 1 - head_);
                    head_ = pos + 1;
                    is_line_head_ = true;
                }

            // 改行文字が現れないままバッファの先頭から末尾まで使い切った場合
            // または force が真の時 バッファの中身を強制的に全て出力する
            // force が偽であり かつバッファの先頭の方に余裕がある場合は
            // 詰めるだけにする
            if (head_ != this->pptr() && this->pptr() == this->epptr())
            {
                const std::size_t   length = this->pptr() - head_;
                if (head_ == this->pbase() || force)
                {
                    if (is_line_head_)
                        os_->write(prefix_, prefix_size_);
                    os_->write(head_, length);
                    this->clear_buffer();
                    is_line_head_ = false;
                }
                else
                {
                    char_type   temp[length];
                    traits_type::copy(temp, head_, length);
                    this->clear_buffer();
                    traits_type::copy(head_, temp, length);
                    this->pbump(length);
                }
            }
        }

    public:
        /**
         * @brief コンストラクタ
         * @param [in] os          対象とする出力ストリーム
         * @param [in] prefix      プレフィックス
         * @param [in] buffer_size バッファのサイズ
         */
        streambuf(
            ostream_type& os, const char* prefix, std::size_t buffer_size)
            : os_(&os), prefix_(prefix),
              buffer_(new char_type[buffer_size]),
              buffer_size_(buffer_size), is_line_head_(true)
        {
            prefix_size_ = traits_type::length(prefix_);
            this->clear_buffer();
        }

        /// デストラクタ
        virtual ~streambuf()
        {
            delete [] buffer_;
        }

    protected:
        /// バッファが溢れた時に呼ばれるメンバ関数
        virtual int_type overflow(int_type c = traits_type::eof())
        {
            this->write_with_prefix(false);

            if (c != traits_type::eof())
            {
                *(this->pptr()) = traits_type::to_char_type(c);
                this->pbump(1);
                return traits_type::not_eof(c);
            }
            else
                return traits_type::eof();
        }

        /// 書き出し先とバッファを同期する時などに呼ばれるメンバ関数
        virtual int sync(void)
        {
            this->write_with_prefix(true);
            return 0;
        }
    };
};

// 頻繁に利用されるであろうクラスを予め型定義しておく.(<iosfwd> 参照)
typedef basic_prefixed_ostream<char>  prefixed_ostream;

#endif  // !define PREFIXED_OSTREAM_H_INCLUDED_

備考

以下のコンパイラでのみ動作確認済み.

  • g++ (GCC) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.0)
  • g++ (GCC) 4.1.2 (Gentoo 4.1.2)

余談

std::basic_streambuf を継承するなんて真似,自力で見出すことは到底不可能だと思うんだけど.

WantToDo

随分長い期間頭の隅にあるんだけども実行に移せていない諸々の事を列挙してみる.

Weblog に関して

  • 以前作ったプラグインの検証・改良をしたい.当初は「どうせ自分専用だろう」と割り切ってたんだけど,そういうわけにもいかなくなったようなので.
  • テンプレート (特にスタイルシート) の検証が不十分な気がする.昔の記事だとレイアウトが崩れるかも知れん.
  • ページを (動的 / 静的にかかわらず) application/xhtml+xml で送出できるようにしたい.しかし Serene Bach 本体のスクリプトを弄る事はしたくない.無理か ?
  • フォーカスが当たると (type="text" な) input 要素や textarea 要素の初期値を消去,っつー超小規模な JavaScript をまだ書いてない.
  • del 要素や ins 要素の datatime 属性を半自動的に追加するような JavaScript あるいは Vim スクリプトも書きたい.
  • a 要素に target 属性を追加してくれやがるプラグインとか title 属性を追加してくれないプラグインとかを修正してやりたい.
  • Text::VimColor モジュールを使って各種コードの色付けをしてくれるプラグインも作りたい.
  • 今使ってるコメントプレビュー機能をプラグイン化したい.
  • Serene Bach 本体が吐き出す JavaScript ファイルに関して調べたい.んで気になるところは弄りたい.

Wiki に関して

  • こっちも application/xhtml+xml で送出できるようにしたい.ページは全て動的生成されるんだから簡単に実現できる気がする.もしかしたら設定を見逃してるだけだったりして.
  • Weblog に対して行ったのと同様にスタイルシートの見直しとかをしたい.
  • もちっと Wiki そのものを活用したい.

研究に関して

  • 早く実験 (シミュレーション含む) できる状態に辿り着かねば !! 慢性的焦燥感.が,焦るばかりで何だか空回ってる気もする.
  • 私用ライブラリを復旧させたい.つってもゼロから作り直すなんて事はしてないけどw
  • いい加減 Numerical Recipes in C から脱却したい.(ラッパークラスで隠蔽してるとはいえ) 1-origin なんてやめてさぁ.LAPACK とかあるんだからさぁ.
  • 過去に自分がまとめた用語集の資料をもちっとマシなものにしたい.今のは具体例が乏しすぎ.
  • んで別の用語に関する資料も作成・追加しときたい.
  • 私用 ebuild を拡充したい.tar ボールとかから手動でインストールしたものがまだ幾つかあったはず….

その他

  • ARMv4T のシミュレータ等を作ってみたいと思い始めてからもう丸 4 年経とうとしてる.本を買った直後は多少手を付けたけど,結局それっきり.
  • 私用 PC の OS の再インストール.何がきっかけだったのか知らんがログイン直後からスタートアッププログラムが全て起動し終わるまでの時間が異様に長い.Vista もアンインストールせな.
  • ついでに Linux on VMware をやめて Cygwin にしようかと思っている.これまでのように自室で UNIX 系ツール群を駆使するような作業をする事があまりないとしても,コマンドライン環境の増強という副次的効果があるので.仮想マシンだとこうはいかない.
  • 服を買いたい.いやまぁ「買えば ?」って話やけどさ.
  • 部屋を片付けたい.いやまぁ「片付ければ ?」って (ry

Serene Bach と文書タイプ

Serene Bach のモジュールファイル群とかプラグインファイル群を読めば読むほど, Serene Bach は 「valid な XHTML 文書を書き出す事を想定していない」 という仕様で実装されているとしか思えない. 管理ページだって DOCTYPE 宣言では XHTML 1.1 を謳っておきながら 未定義の target 属性を使ってるし, テンプレートの文書タイプが何であろうが 記事作成支援の一環として target 属性の値の入力を勧めるダイアログボックスが出たりするし.

モジュールとかに手を加えることなく 生成される記事が valid な文書であるようにしたいんなら, テンプレートを HTML 4.01 Strict/Transitional 準拠で作るのが最も手っ取り早いかもしれない. プラグイン (特に JavaScript を使うもの) の仕様によっては XHTML 1.0 Transitional 準拠も通じるかもしれない. 「いや 何が何でも valid な XHTML 文書を生成したいんだ」 っつーなら, Serene Bach を大改造するか 他の Weblog ツールに乗り換えることになるんだろう.

細部まで検証したわけじゃないんで, 以上の話はあくまでも推測ね 推測.

Serene Bach とコンテントネゴシエーション

この Weblog のテンプレートは XHTML 1.1 で作ってるんだけど, メディアタイプは text/html として Web サーバから送出されている. これをブラウザの要求に応じて application/xhtml+xml として送出できんかいな, あわよくばそういうプラグインを作れんかいな, と これもまた前々から思っているところ.

ということで, 昨日に引き続き Serene Bach のモジュールを目で追っかけていった. 直接関係しそうなのは下記の 1 行ということを把握. 但し 「1 行」 とは言っても, これと同様の行が複数のモジュール, 複数のメソッドに記述されている.


return sb::Interface->get->head('type'=>'text/html') . $self->set_main($cms->output);

…凄いなコレ. テンプレートから生成された文書 (≒ メソッド sb::TemplateManager::output の戻り値 $cms はクラス sb::TemplateManager のオブジェクト.) が何であろうが関係なく 'text/html' だなんて. せっかくメディアタイプを指定できるように sb::Interface::head が実装されているのに これじゃ意味ねーじゃん.

オブジェクト指向的には, テンプレートのメディアタイプを判別して出力する sb::TemplateManager::get_type なるメソッドを追加すると良さげ. これをプラグイン追加の形で実現するのは簡単だろうけど, あちこちに散在している上記 1 行の修正もプラグイン追加の形で, つまり 1 行を書き換えるためだけにメソッドをオーバーライドする, てのは非現実的だなぁ.

また明け方になって就寝するのはマズいので今回はここまで. 近いうちに作業しようと思う.

GCC と Gentoo

先月は研究室ライブラリとか自作プログラムとかを書き直しまくってた.MIRU に発表に行く前に発覚してた問題があったんで,そのついでに全部見直してみよう,と.実は後者はまだ終わっていない.

書き直した後のプログラムとかが問題なくコンパイルできるか確認しようと思って,確か 28 日に GCC 4.1.1 のインストールを決行した.これまで使ってたのは 3.4.6.インストール作業自体は実に簡単なもので,旧バージョンとの共存もあっけなく実現した.で,実際にコンパイルしてみると特に問題はなかった.まあ,テンプレートはおろか継承すら使ってないものがほとんどだからなぁ.

じゃあ次はシステム全体を 4.1.1 で再コンパイルしよう,ということで,gcc-config でコンパイラを切り替えて emerge -e world.途中 libquicktime-0.9.4 のコンパイルに失敗してたからこれを emerge -C して再び emerge -e world.二度手間.超無駄.emerge --skipfirst --resume なら二度手間にならずに済むという事を知ったのは終了後 orz ということで丸一日費したのが確か 29 日.コンパイラのバージョンを上げたからといって 何かが劇的に向上したという事は特になく.

2, 3 日くらい疎かにしてた emerge --sync をさっき実行して emerge -pvuD world してみたら glibc のバージョンが上がってた.もしやと思って gentoo.org に飛んでみたら,2006.1 を 30 日にリリースしたとのニュースが.この 2006.1 は GCC 4.1 を元々含んでたわけで.それを俺はたった 1, 2 日ほど早まってインストールしただけなわけで.なんか癪やわぁ.

doxygen にハマる

以前から興味があった doxygen を今日ようやく使ってみた.“ソースとドキュメントの一元化”って素晴らしい.もしこの魅力を B3 の時に知っていたら,実験で提出を求められた仕様書を作成する気力なんて更になくなってただろうね.

ここで覚え書き.
クラステンプレートのフレンド関数のプロトタイプには関数名の直後に <T> を書く必要がある (gcc 3.3.2 でコンパイル成功を確認済み) んだけども,


/// RGB を扱うクラステンプレート (一部省略)
template <typename T> class Color
{
public:
    T   red, green, blue;
    /**
     * @brief RGB をストリームに書き出す
     * @param [in,out] os 出力ストリーム
     * @param [in] col Color<T> 型オブジェクト
     * @return 出力ストリーム
     */
    friend std::ostream&  operator << <T>(std::ostream& os, const Color<T>& col);
}

のようなソースから doxygen によって作成される HTML ドキュメントでは,operator << () 関数に限って << が削除されてしまう (doxygen 1.4.3, 1.4.6 で共に確認).<T>を削除して再度 doxygen に通せば正しい出力が得られたから,これはたぶん doxygen のバグなんだろう.