プログラミング

2010年7月23日 (金)

お気軽にXMLを扱ってみる 其の3

ずいぶん、横道にそれていましたが、お気軽にXMLを扱ってみたいと思います。

まずは、属性クラスを設計します。

属性は、名前と値を管理するだけですので、さくっとできます。
以下、ソースリストです。

class attribute {
public:
    /* 名前の取得 */
    std::string getName( void ) const {
        return m_Name;
    }
    /* 値の取得 */
    std::string getValue( void ) const {
        return m_Value;
    }
    /* 名前の設定 */
    void setName( string name ) {
        m_Name = name;
    }
    /* 値の設定 */
    void setValue( string value ) {
        m_Value = value;
    }
public:
    /* コンストラクタ */
    attribute() {;}
    /* デストラクタ */
    virtual ~attribute() {;}
protected:
    attribute( const attribute& );
    const attribute& operator=( const attribute& );
};

解説するまでもないですね。

短いですが、今回はここまで。次回は、要素を管理するクラスを設計します。

| | コメント (0) | トラックバック (0)

2010年5月29日 (土)

お気軽にXMLを扱ってみる 其の2

では、XMLを扱うクラスを設計する前に、XMLはどういう構造なのか調べてみます。

まずは、XML宣言です。
XMLであることを明確に示す宣言で、<?xml ~ ?>で表します。この宣言を明記する場合は、必ず先頭に置かなければなりませんが、なくても構わないそうです。

続いて、要素です。
XMLにおいて最も基本となる情報で、下記の様に表します。

<nanika></nanika>

といった感じです。
また要素の間には、コンテンツを挟むことができます。

<nanika>
  <ahaha>heyhey</ahaha>
</nanika>

上記の場合だと、『heyhey』がコンテンツになります。
コンテンツを持たないものは空要素となります。この場合、2つの表現方法があります。

<nanika></nanika>
<nanika/>

といった感じです。

続きまして、属性です。
属性は、要素の付属情報という位置づけで、属性の中に記述します。

<nanika no="1"/>

この場合、『nanika』要素に『no』という属性を付加しています。
また、『no』属性の値は『1』になります。

属性は、複数記述することができます。

<nanika no="1" name="nanika"/>

ほかにも、いろいろあるみたいですが、これくらいにしておきましょう。一応、これぐらいでも、最低限XMLっぽいものが表現できそうですので。

ここまでのことをまとめますと、

宣言は、XMLの先頭に置くが、なくてもよい。
要素は、1つのコンテンツをもつ。
要素は、複数の要素をもつ。
要素は、複数の属性をもつ。
属性は、1つの値をもつ。

といったところでしょうか。

次回、この構造をもとに、XMLを扱うクラスを設計します。

| | コメント (0) | トラックバック (0)

2010年5月22日 (土)

お気軽にXMLを扱ってみる 其の1

そういえば、最近INIファイルって見かけなくなってきたような気がします。
それでちょこっと調べてみると、どうやら過去の資産となりつつあるようですね。

.net frameworkからはINIファイルを扱う機能はなく、その代わりにXMLファイルを使用することを推奨しているそうです。

じゃあ、XMLはプログラミング言語によってどのような扱いになっているのかといいますと、

JAVAなどは、はじめからXMLが扱える標準関数があるみたいですね。

C++なんかだと、標準でXMLを読むことはできず、『Xerces-C++』等のライブラリを使用することで、XMLが扱えるようになります。

お気軽に作ったアプリ(そもそもC++である時点でお気軽ではないような気もしますが・・・)の情報を保存するのにXMLを使おうと思った瞬間、Xerces-C++ライブラリを入れてとかやると一気にお気軽感が薄れる感じがします。

そこで、C++でお気軽にXMLを扱えるようにしてみたいと思います。

お気軽にXMLを扱うので、DTDとかは無視。XMLの構造も実装が面倒なときは、XML自体に制限(コメントの『<!-- -->』未サポートとか)をかけてしまったりするかもしれません。

ということで、まずは、XMLの情報を保持するためのクラス設計をします。

| | コメント (0) | トラックバック (0)

2010年3月 6日 (土)

Factory Method(ファクトリーメソッド)でNULLチェックをなくした

クラスで使用するオブジェクトが初期化されていないときは、何もしないでね。みたいな場面は多々としてあると思います。

コードで書くと、以下のような感じです。

void CNanika::proc(void) {
    // オブジェクトのNULLチェック     if(m_Ctrl == NULL ) {
        return;
    }
    // メイン         ・     string name = m_Ctrl->getName();         ・         ・ }

一つや二つの関数だったらいいですけれども、たくさんあったら面倒くさいですね。なによりも、抜けとかあったら、とたんにアクセスエラーですから。

目標としましては、オブジェクトのNULLチェック部分を追い出してみたいと思います。さて、どのように追い出しましょうか。まず、処理を行う/行わないクラスに分けますか。

とりあえず、処理を行うクラスをCNanikaクラスで、行わないクラスをCNanika_NULLクラスとしましょう。

CNanikaクラスとCNanika_NULLクラスの関数定義は一致していなければならないので、関数定義を集めたCNanika_Interfaceクラスを基底クラスとして、CNanika, CNanika_NULLクラスに継承させます。

とりあえず、用意する関数は、上記コードにも書いたproc()だけとします。

コードにします。

class CNanika_Interface {
public:
    virtaul void proc( void ) = 0;
protected:
    virtual ~CNanika_Interface() {}
};

class CNanika_NULL : public CNanika_Interface {
public:
    virtaul void proc( void ) { return; }
protected:
    CNanika_NULL() {}
    virtual ~CNanika_NULL() {}
};

class CNanika : public CNanika_Interface {
public:
    virtaul void proc( void ) {
        // メイン             ・         string name = m_Ctrl->getName();             ・             ・     } protected:
    CNanika() {}
    CNanika( CCtrl* ctr ) : m_Ctrl( ctr ) {}
    virtual ~CNanika() {}
protected:
    CCtrl* m_Ctrl;
};

あとは、CCtrlがNULLかどうかでCNanika_NULLもしくは、CNanikaクラスオブジェクトを生成して、CNanika_Interfaceを経由して提供してあげれば、良さそうです。あと、CNanika_NULLとCNanikaクラスは勝手に生成されてはこまるので、コンストラクタをprotectedに配置しています。

あとは、生成部だけですが、生成部には、factory methodパターンを使います。

factory methodをwikiで調べてみると、『オブジェクトの生成をサブクラスに委ねることによって、プログラム内で使用されるインスタンスの型の制約を緩める』ということみたいですね。

これは、CNanika, CNanika_NULLクラスを生成するクラスを別に用意するということになります。とりあえず、そのクラスは、CNanika_Factoryとしましょう。あとは、CCtrlのありなしでCNanikaかCNanika_NULLを生成してあげればいいのです。

では、コードです。

class CNanika_Factory {
public:
    // 生成     static CNanika_Interface* create( CCtrl* ctr ) {
        if( ctr != NULL ) {
            return new CNanika( ctr );
        }
        else {
            return new CNanika_NULL;
        }
    }
    // 破棄     static void release( CNanika_Interface* obj ) {
        if( obj != NULL ) {
            delete obj;
        }
    }
};

生成部では、ctrの中身をチェックして、NULLじゃなければ、CNanikaを生成して返します。NULLの場合は、CNanika_NULLとなります。

あと、生成を用意したので、破棄も用意してあげます。

このままでは、CNanika_Factoryは、CNanikaとCNanika_NULLを生成できない(CNanikaとCNanika_NULLのコンストラクタがprotected属性であるため)ので、CNanika, CNanika_NULLにfriend classを追加して、完成です。

以下、完成コードになります。

class CNanika_Factory;

class CNanika_Interface {
    friend class CNanika_Factory;
public:
    virtaul void proc( void ) = 0;
protected:
    virtual ~CNanika_Interface() {}
};

class CNanika_NULL : public CNanika_Interface {
    friend class CNanika_Factory;
public:
    virtaul void proc( void ) { return; }
protected:
    CNanika_NULL() {}
    virtual ~CNanika_NULL() {}
};

class CNanika : public CNanika_Interface {
    friend class CNanika_Factory;
public:
    virtaul void proc( void ) {
        // メイン
            ・
        string name = m_Ctrl->getName();
            ・
            ・
    }
protected:
    CNanika() {}
    CNanika( CCtrl* ctr ) : m_Ctrl( ctr ) {}
    virtual ~CNanika() {}
protected:
    CCtrl* m_Ctrl;
};

class CNanika_Factory {
public:
    // 生成
    static CNanika_Interface* create( CCtrl* ctr ) {
        if( ctr != NULL ) {
            return new CNanika( ctr );
        }
        else {
            return new CNanika_NULL;
        }
    }
    // 破棄
    static void release( CNanika_Interface* obj ) {
        if( obj != NULL ) {
            delete obj;
        }
    }
};

今回は、一関数のみのNULLチェックを追い出しただけですので、コード増えすぎ、煩雑になりすぎって感じですが、たくさんの関数のNULLチェックを追い出すときは、結構有用ではないかと思っています。

| | コメント (0) | トラックバック (0)

2010年1月19日 (火)

浮動小数を文字列に変換してみる

前回、整数を文字列に変換しましたので、今回は、浮動小数を文字列に変換してみます。

さて、浮動小数はどうやって文字列に変換すればいいのでしょうか。以下に変換方法を示します。

1. 整数部と小数部に分ける。
2. 整数部はそのまま文字列に変換する。
3. 小数部は10^nをかけて整数にし、文字列に変換する。
4. 整数部文字列 + "." + 小数部文字列な形に整形する。

それでは、一つ一つ、実現していきます。

まず、整数部と小数部に分けます。整数部の取り出しは、簡単です。floatをintでキャストするだけです。文字列の変換は、前回作った関数を呼び出しています。

float fVal = 1.13f;
unsigned int up = static_cast<unsigned int>( fVal );
std::string strUp = Int2String( up, RADIX_DEC, 0, ' ' );

小数部の取り出しは、難しくないのですが、厳密にやろうとするといろいろと大変です。このあたりは、floatの誤差の話が絡みますので、厳密ではなく、お手軽にやってしまいます。

いろいろとやり方があるのですが、今回は、1で割った余りを求めるやり方でいきます。

float fTemp = ::fmodf( fVal, 1.0f );
unsigned int down = static_cast<unsigned int>( fTemp * 100 );
std::string strDown = Int2String( down, RADIX_DEC, 2, '0' );

::fmodf()は標準ライブラリの関数で浮動小数点数の剰余を計算するものです。上記のコードは、小数第2位までの値を取得し、文字列に変換しています。

あとは、整形するだけです。

std::string result = strUp + "." strDown;

これで、完了です。resultに浮動小数⇒文字列の変換結果が入ります。

 

そういえば、符号のことを忘れていたのですが、符号に関しては、一番最初に0.0fより小さいかで符号かつくのか判定し、判定後、絶対値をとって文字列に変換し、整形時に符号が付くか付かないかで'-'を先頭につけたり、つけなかったりしてあげればOKです。

| | コメント (0) | トラックバック (0)

2009年12月29日 (火)

整数を文字列に変換してみる

前回、boostを使って、整数を文字列に変換してみたのですが、どうも処理が重いようです。いや、計測したわけではないので、感覚的なのですが。まぁ、いろいろできるので、多少重いのは、当たり前のことなのでしょう。

ということで(全然繋がっていませんが)、今回は、整数を文字列に変換する関数を作ってみます。

機能としましては、以下の様にします。
・整数を文字列に変換できる。
・2進数、8進数、10進数、16進数の指定ができる。
・桁数を指定できる。
・空いた箇所に埋める文字を指定できる。

まずは、関数名を考えましょう。まぁ、さらっと『Int2String()』とします。戻り値は、string。引数は、機能から考えて、以下のようになるでしょうか。
・変換元整数
・○進数指定
・桁数指定
・空白文字指定

変換元整数、桁数指定、空白文字指定は特に問題ないのですが、○進数指定がちょっとやっかいですね。 普通にint型とかでやられると、「300進数でやれ」みたいな指定も可能となってしまいますね。まぁ、引数チェックではじけばいいのですが・・・。

○進数については、最初から、規定のものしか指定できないようにしてしまいましょう。といっても、列挙型を定義するだけですが。

まず、○進数指定の列挙型の定義から。

enum RADIX {
    RADIX_BIN = 2,    /* 2進数 */
    RADIX_OCT = 8,    /* 8進数 */
    RADIX_DEC = 10,      /* 10進数 */
    RADIX_HEX = 16,      /* 16進数 */
};  /* enum */

となります。

ここまでできれば、関数を定義できますね。では、さくっと。

string Int2String( unsigned int _val, RADIX _r, unsigned int _dig, char _fill_char )

簡単な引数の説明を。『_val』は変換元整数、『_r』は○進数の指定、『_dig』は桁数、『_fill_char』は空白文字指定となります。

それでは、整数から文字列変換に入りましょう。

整数を文字に変換するテーブルを定義します。

static const char* CHARACTER_TABLE = "0123456789ABCDEF";

たとえば、このテーブルの7番目を参照すると、『7』という文字が取得できるということになります。 あとは、変換元整数値を1桁ずつ取得して、取得した数値を使って文字を取得し、取得した文字をバッファに積んでいけば、整数を文字列にすることができるのです。

では、コード化です。

string str;
int dig_temp = 0;
if( _val == 0 ) {
    str = '0';
}
else {
    for( ; _val != 0; dig_temp++ ) {
        str = CHARACTER_TABLE[_val%_r] + str;
        _val /= _r;
    }
}

整数値から1桁ずつ取得するには、整数値から○進数の値の余りを求めます。その後、○進数で整数値を割り、その商を使って、余りをもとめて・・・みたいな感じで0になるまで繰り返します。

あとは、桁を埋めればいいので、

for( ; dig_temp < _dig; dig_temp++ ) {
    str = _fill_char + str;
}

となります。

ということで、ようやく完成です。

では、最後にソースコードを。

/* ----- 文字列変換テーブル ----- */
static const char* CHARACTER_TABLE = "0123456789ABCDEF";

enum RADIX {
    RADIX_BIN = 2,    /* 2進数 */
    RADIX_OCT = 8,    /* 8進数 */
    RADIX_DEC = 10,      /* 10進数 */
    RADIX_HEX = 16,      /* 16進数 */
};  /* enum */

/* ----- 整数 ⇒ 文字列 ----- */
string Int2String( unsigned int _val, RADIX _r, unsigned int _dig, char _fill_char ) {
    string str;
    int32 dig_temp = 0;
    /* ----- 文字列変換 ----- */
    if( _val == 0 ) {
        str = '0';
    }
    else {
        for( ; _val != 0; dig_temp++ ) {
            str = CHARACTER_TABLE[_val%_r] + str;
            _val /= _r;
        }
    }
    /* ----- 桁埋め ----- */
    for( ; dig_temp < _dig; dig_temp++ ) {
        str = _fill_char + str;
    }
    return str;
}

以上です。

| | コメント (0) | トラックバック (0)

2009年12月19日 (土)

ちょっと書式指定文字列を調べた

int A = 10; という変数があって、このAの内容を3桁0埋めで文字列化したいとします。つまるところ、『010』となればOKです。

ぱっと思い浮かぶのは、以下のようなものでしょうか。

char temp[256];
::sprintf( "%03d", A );

う~ん、いかにも、C言語ですね。やっぱり、文字列を扱うならstd::stringを使いたいですね。では、もう一歩前進して、C++っぽくいきます。

std::stringstream stream;
stream << std::setw( 3 ) << std::setfill('0') << A;
std::string temp = stream.str();

ストリームを作ってデータを流し込んであげます。このコードは、::sprintf()を使うのと比べて、大きな利点があります。なんといっても、文字列の容量を気にしなくても良い点でしょう。::sprintf()を使用する場合、文字列を入れるバッファを指定しますが、文字列がバッファの容量を超えてしまってはいけないのです。

でも、書式指定がちょっとわかりにくいですね。std::setw()で出力幅を指定して、std::setfill()で空きには、0を入れるというような長いコードをかかなくてはいけないですから。上記コードは1変数の変換のみですけど、いろんな書式を10個ぐらい変換するといったら、軽くパニックですね。::sprintf()だったら、『%d』とか『%3d』とか『%02X』とか指定すればいいので、楽かつ、わかりやすいのですが。

では、さらに、もう一歩進みましょう。

ここで、Boostのformatを使ってみます。以下、コードです。

std::stringstream stream;
stream << boost::format( "%03d" ) % A;
std::string temp = stream.str();

書式指定部分が::sprintf()みたいになり、すっきりしました。boost::format()で書式を指定したあとに、変換対象物を流し込むのですが、ここで、『<<』ではなく、『%』になっています(Aの送り先は、streamではなく、boost::format()になります)ので、注意が必要です。

すっきりしたので、今回はここまで。

ちなみに、Boostというのは、『boost wiki』みたいな感じでググってみてください。

| | コメント (0) | トラックバック (0)

2009年11月21日 (土)

ローテク?お手軽?古典的タスクシステムの設計 其の4

それでは、での実装です。

まず、リスト構造にしなくてはいけないのですが、これは、配置newで作った双方向リストのを使います。そして、処理関数を純粋仮想関数とすればいいので、以下のようになります。

template<class T> class task_class {
private:
    static void* raw_memory;        /* メモリプール */
    static long* empty_array;        /* 空き番号配列 */
    static long* empty_position;    /* 空き位置指定 */
    static T* top;                /* リンク先頭 */
private:
    long position;       /* 確保位置 */
    T* next;            /* 次のリスト */
    T* prev;            /* 前のリスト */
protected:
    /* ----- コンストラクタ ----- */
    task_class() {}
    /* ----- デストラクタ ----- */
    virtual ~task_class() {}
public:
    /* ----- 初期化 ----- */
    static void initialize( long _size );
    /* ----- 後始末 ----- */
    static void dispose( void );
    /* ----- すべて削除 ----- */
    static void deleteAll( void );
    /* ----- 挿入 ----- */
    static T* add( void );
    /* ----- 削除 ----- */
    static void remove( T* _lt );
public:
    /* ----- 処理関数 ----- */
    virtual void process( void ) = 0;
};  /* class */

初期化や後始末等の関数内容は全く変わらないので、ヘッダーだけです。

この関数を派生してprocess()関数を実装します。呼び出しは、やはり双方向リストで作成したイテレータクラスを利用します。たとえば、sample_taskというタスククラスを作ったとして、全タスクを呼び出すには以下のようになります。

for( task_iterator<sample_task> it; it.has(); ) {
    it.next()->process();
}

イテレータクラスは、今回の内容に合わせて、task_iteratorとしました。

これだけだと、ちょっと実用性にかけますので、タスク自身が用済みになったときに、タスクリストから外す処理を加えてみます。

まず、タスククラスの処理関数を

virtual bool process( void ) = 0;

として、戻り値が『true』のときは、処理を続行、『false』のときは、処理完了とします。

あとは、process()関数が『false』を返したときにそのタスクをリストから外せばいいので、呼び出し部が

for( task_iterator<sample_task> it; it.has(); ) {
    if( !it.next()->process() ) {
        it.remove();
    }
}

となります。

タスクシステムとしては、最小限の機能しか持ち合わせていませんが、一応完成しました。

以前、業務系アプリケーションを作ったとき、機器の監視や通信等々、リアルタイム性が要求される部分をタスクにして処理していました。こんなものは別スレッドにして 並列化にするのが普通じゃないのですかといえば、そうかもしれませんが、並列化には、同期とか排他とかがつきまといます。そういった場合、やはりちゃんと設計しておかないと、後々、並列化に絡んだバグに悩まされることになります。

並列が必須でなければ、お手軽に古典的タスクシステムなんていかがでしょうか。

| | コメント (0) | トラックバック (0)

2009年11月 7日 (土)

ローテク?お手軽?古典的タスクシステムの設計 其の3

今回は、タスクシステムをC++に実装していきたいと思います。

C++はC言語の上位コンパチですので、前回のものはC++でも普通にできちゃったりします。

これで、終了とすると、何の芸もないので、ここは一つ、C++っぽくしてみましょう。

まず、TCBですが、C言語では構造体で表現していました。今回は、C++ですから、クラスを使ってみましょうか。このTCBは、お互いをリンクリストで繋いでいました。まぁ、このあたりは変えなくてもいいですね。

ワークエリアは、クラスなんですから、自由に定義してもらって構わないでしょう。また、処理関数については、 関数へのポインタなどというものは使わず、純粋仮想関数としましょう。

ということで、処理関数を純粋仮想関数とし、リンクリスト構造を持った基本クラスを作成します。ワークエリアにあたる 部分は、基本クラスを派生して、自由にメンバ変数を定義するという形とします。

以上のことを、図にしてみました。

Clip_2

次回、C++で実際に実装してみます。

| | コメント (0) | トラックバック (0)

2009年10月24日 (土)

ローテク?お手軽?古典的タスクシステムの設計 其の2

今回は、古典的タスクシステムについて記述していきます。

まず、タスクとはということですが、普通に調べますと、OSから見た処理の実行単位とか、処理の単位ということらしいです。古典的タスクシステム上であれば、処理の単位というのが近いでしょうか。

古典的タスクシステムでのタスクは、処理関数とワークエリアを組み合わせたものになります。

Fig1

このタスクを次々と実行していくシステムを古典的タスクシステムといいます。

Fig2

では、これらをC言語で実装することを考えてみましょう。

まず、タスク自体は構造体で表現します。処理関数は何で表現するか。これは、処理関数へのポインタとしてあげれば、問題なさそうです。ワークエリアは十分な容量だけ確保しておいて、然るべき形にキャストして使います。

タスクシステムと呼ぶには、タスクを次々と実行していかなければならないのですが、これはリスト構造を使えば、解決します。具体的には、次のタスクへのポインタと 前のタスクへのポインタを用意して、タスク間を繋いであげればいいわけです。これらを図にすると、以下のような感じです。

Fig3

タスク一つを構成する構造体をTCB(Task Control Block)と呼びます。このTCBをコードにすると以下のようになります。

/* ----- TCB ----- */
struct TASK {
    /* ----- 処理関数 ----- */
    void ( *Func )( TASK* _tsk );
    /* ----- 次 ----- */
    TASK* next;
    /* ----- 前 ----- */
    TASK* prev;
    /* ----- ワークエリア ----- */
    char work[256];
};

ワークエリアは適当な容量で確保しました。

では、このTCBが存在しているものとして、タスクを実行してみましょう。タスクはリストで繋がっていますので、リストを伝って、それぞれのタスクの処理関数を呼び出すだけです。タスクの先頭は、TOP_TASKに入っていて、終端は、nextがNULLのものとします。

for( TASK* tsk=TOP_TASK; tsk != NULL; tsk=tsk->next ) {
    ( *tsk->Func )( tsk );
}

以上、簡単ですが、古典的タスクシステムを紹介しました。本格的なものは、タスクの優先順位があったりします。

次回は、これらをC++で実装していくことを考えていきたいと思います。

| | コメント (0) | トラックバック (0)

より以前の記事一覧