marunomaruno-memo

marunomaruno-memo

[C++] メンバー関数の関数ポインターをメンバー変数にする

2014年06月20日 | C / C++
                                                        2014-06-20 新規
[C++] メンバー関数の関数ポインターをメンバー変数にする
======================================================================

(MinGW gcc (GCC) 4.5.4)

クラスのメンバー関数への関数ポインターの定義と使用。


□ funcpointer.cpp
---
#include <iostream>
using namespace std;

class A {
private:
    int data;
    int (A::*fp[2])(int data);  // (1) インスタンス関数のポインター

public:
    A(int data) :
            data(data), fp({&A::func, &A::func2}) { // (2)
    }

    int exec(int index, int data) {
        return (this->*fp[index])(data);    // (3)
    }

private:
    int func(int data) {
        return this->data + data;
    }
    int func2(int data) {
        return this->data - data;
    }
};

int main() {
    A a(100);
    cout << "0: " << a.exec(0, 10) << endl; // (4)
    cout << "1: " << a.exec(1, 20) << endl;

    return 0;
}
---

□ 実行結果
---
0: 110
1: 80
---

(1) インスタンス関数ポインターの配列

通常のメンバー関数ポインターの配列と同じ。

    int (A::*fp[2])(int data);  // (1) インスタンス関数のポインター


(2) インスタンス関数ポインターの配列の初期値

    A(int data) :
            data(data), fp({&A::func, &A::func2}) { // (2)
    }

コンストラクターの初期化リストで、
    fp({&A::func, &A::func2})
ということはできないかと思ったが、どうやらできる。
ただし、つぎの警告が出る。
    extended initializer lists only available with -std=c++0x or 
    -std=gn
u++0x
コンパイルするときに上記のオプションを指定すれば、警告は出なくなる。


(3) 関数ポインターの配列を実行するメンバー関数

    return (this->*fp[index])(data);    // (3)

this ポインターを使わないといけない。使わずに、
    (fp[index])(data)
とすると、つぎのコンパイルエラー。
    must use '.*' or '->*' to call pointer-to-member function in 
    '((A*)this)->A::fp[index] (...)', e.g. 
    '(... ->* ((A*)this)->A::fp[index]) (...)'

メンバー関数のポインターは、".*" か "->*" を使わないといけないからかな。


(4) 実行

たんに、メンバー関数を呼んでいるだけ。最初の引数はメンバー関数のポイン
ターの配列の添字を指定している。
    cout << "func(): " << a.exec(0, 10) << endl; // (4)

メンバー関数のポインターの配列 fp を public にして、main() 関数でつぎの
ように使おうとしても、
    (a.*fp[0])(30)
コンパイルエラー。
    'fp' was not declared in this scope
まあ、上記の書き方だと、main 関数の中で fp を定義しないとだめだ。


                                                                   以上


[C++] 関数テンプレートのポインター

2014年06月19日 | C / C++
                                                        2014-06-19 新規
[C++] 関数テンプレートのポインター
========================================================================
(MinGW gcc (GCC) 4.5.4)

□ functionpointer_template.cpp
---
#include <iostream>
using namespace std;

template<class T>   // (1)
T square(T a) {
    return a * a;
}

template<class T, class F>  // (2)
T calc(T a, F func){
    return func(a);
}

int main() {
    int (*p)(int) = square; // (3) この使い方はあまり意味ない感じ
    cout << p(2) << endl;   // (4)
    cout << p(3.4) << endl; // (5) 

    cout << calc(2, square<int>) << endl;   // (6) 
    cout << calc(3.4, square<double>) << endl;  // (7)
//  cout << calc(5, square) << endl;    // コンパイルエラー

    return 0;
}
---

□ 実行結果
---
4
9
4
11.56
---

(1) 関数テンプレートの宣言

(2) 関数テンプレートのポインターを引数に持つ関数テンプレートの宣言

(3) 関数テンプレートを実体化したものを関数ポインターに設定

関数ポインターは、通常の関数ポインターと変わりはない。テンプレートの型は
自分で設定している。
    int (*p)(int) = square; // (3) この使い方はあまり意味ない感じ


(4)(5) 関数ポインターの利用

int 型の引数を取るので、そのまま使う。
    cout << p(2) << endl;   // (4)

int 型の引数を取るので、double 型の引数を渡しても、int 型として処理され
る。
    cout << p(3.4) << endl; // (5) 


(6)(7) 関数テンプレートのポインターを引数に持つ関数テンプレートの利用

int 型を明示しているの、int 型の引数の square 関数ポインターを渡す。
    cout << calc(2, square<int>) << endl;   // (6) 

double 型を明示しているの、double 型の引数の square 関数ポインターを渡す。
    cout << calc(3.4, square<double>) << endl;  // (7)

つぎのように、データ型を明示していないと、コンパイルエラーになる。
    calc(5, square)

    no matching function for call to 'calc(int, <unresolved 
    overloaded function type>)'

                                                                   以上


[C++] メンバー関数の関数ポインター

2014年06月13日 | C / C++
                                                        2014-06-17 追記
                                                        2014-06-13 新規
[C++] メンバー関数の関数ポインター
========================================================================

(MinGW gcc (GCC) 4.5.4)

クラスのメンバー関数への関数ポインターの定義と使用。


□ funcpointer.cpp
---
#include <iostream>
using namespace std;

class A {
private:
    static int staticdata;
    int data;

public:
    A(int data) :
            data(data) {
    }

    static int staticfunc(int data) {
        return data - staticdata;
    }

    int func(int data2) {
        return data + data2;
    }
};

int A::staticdata = 777;

int main() {
    A a(111);

    cout << "staticfunc(): " << A::staticfunc(999) << 
endl;
    cout << "func(): " << a.func(222) << endl;

    cout << " 関数ポインター " << endl;
    int (*sfp)(int data) = A::staticfunc;   // (1) static 関数ポインター
    cout << "staticfunc(): " << sfp(888) << endl;   //
 (2)

    int (A::*fp)(int data2) = &A::func; // (3) インスタンス関数のポ
インター
    cout << "func(): " << (a.*fp)(333) << endl; // (4)

    return 0;
}
---



□ 実行結果
---
staticfunc(): 222
func(): 333
関数ポインター
staticfunc(): 111
func(): 444
---

(1) static 関数ポインターの定義と初期化

    int (*sfp)(int data) = A::staticfunc;

通常のグローバル関数の関数ポインターと同じように定義する。
関数ポインターは、
    クラス名 :: 関数名
という形で取得する。

定義部分には、クラス名の要素が入っていないので、他のクラスでも同じシグネ
チャーの関数なら、この関数ポインターを使うことができる。

たとえば、B クラスに同じシグネチャーの
    static int staticfunc(int data)
があれば、
    sfp = B::staticfunc;
として、関数ポインターを設定できる。


(2) static 関数ポインターの利用

    cout << "staticfunc(): " << sfp(888) << endl;

通常のグローバル関数の関数ポインターと同じように利用すればよい。


(3) インスタンス関数ポインターの定義と初期化

関数ポインター名の前に、「A::」をつけておく。

    int (A::*fp)(int data2) = &A::func;

スコープ解決しないと、つぎのコンパイルエラーが出る。
    cannot convert 'int (A::*)(int)' to 'int (*)(int)' in initialization

関数ポインターは、
    & クラス名 :: 関数名
という形で取得する。「&」がないとコンパイルエラーになる。
    invalid use of non-static member function 'int A::func(int)'


(4) インスタンス関数ポインターの利用

    cout << "func(): " << (a.*fp)(333) << endl;

インスタンス a を使う。したがって、少し、違和感はあるが、次のような形で
利用する。
    (インスタンス名 .* 関数ポインター名)(引数のリスト)

インスタンスがないと使えないので、
    fp(333) 
では、つぎのコンパイルエラーが出る。
    must use '.*' or '->*' to call pointer-to-member function in 
'fp (...)', 
    e.g. '(... ->* fp) (...)'

もちろん、クラス A のメンバー関数用なので、まったく同じシグネチャーを持
つ別のクラスのメンバー関数のインスタンスで使おうとしても、コンパイルエ
ラー。
    B b;
    (b.*fp)(333)
では、つぎのエラー。
    pointer to member type 'int (A::)(int)' incompatible with object 
type 'B'



■ メンバー関数ポインターのポリモーフィズム (2014-06-17 追記)

上記 A クラスを継承した D クラスをつきのように定義する。

---
class D : public A {
public:
    D(int data) :
            A(data) {
    }

    int func(int data) {
        return data * 2;
    }

};
---

このメンバー関数 func() は、基底クラス A のメンバー関数 func() をオー
バーライドしている。
この D クラスのインスタンスを基底クラス A 型として扱う。

つぎのようなコードを上にサンプルに追加する。
---
    A* d = new D(11);   // (5)
    fp = static_cast<int (A::*)(int)> (&D::func);   // (6)
    cout << "func(): " << (d->*fp)(333) << endl; // (7)
---

この部分の実行結果としては、つぎのようになる。
---
func(): 666
---

A クラスの型だが、D クラスのメンバー関数 func() を実行しているのが確認で
きる。



(5) A クラスの型として、D クラスのインスタンスを生成する。

    A* d = new D(11);   // (5)


(6) D クラスのオーバーライドしているメンバー関数 func を、A クラスの関数
ポインターに代入する

    fp = static_cast<int (A::*)(int)> (&D::func);   // (6) 

このとき、アップキャストすることになるが、不思議なことに、static_cast が
必要。


(7) D クラスのインスタンスを使って、この関数ポインターが指す関数を実行す
る

    cout << "func(): " << (d->*fp)(333) << endl; // (7)


インスタンスのポインター d を使う。したがって、少し、違和感はあるが、次
のような形で利用する。
    (インスタンスのポインター名 ->* 関数ポインター名)(引数のリスト)


                                                                   以上
---
2014-06-17 追記 メンバー関数のポリモーフェィズム
---



[C] 可変長引数の関数から可変長引数の関数を呼ぶ

2013年10月27日 | C / C++
                                                                2013-10-27 新規
[C] 可変長引数の関数から可変長引数の関数を呼ぶ
================================================================================

可変長引数の関数から、別の可変長引数の関数を呼び出す。

gcc の拡張機能で、可変長マクロもあるが、あくまでも gcc 拡張なので、可変長引数を
持つ関数を使う。

サンプルとしては、デバッグ用のプリント関数をつくることにする。

デバッグ用なので、マクロ名 NDEBUG が定義されているときは何もしないようにする。そ
して、プリントした後に改行し、flush も行う。

また、プリントする関数なので、標準ライブラリー関数 printf() のように、可変長の引
数を受け取るようにする。

仕様としては、以下のとおり。
    void debugPrint(const char* tag, const char* format, ...);

    引数
        tag     タグ。
        format  プリントする書式
        ...     可変長引数部分。プリントしたい式や値

なお、この関数の中から、可変長リストを引数にとる関数を呼び出すことにする。
仕様としては、以下のとおり。
    void print(const char* tag, const char* format, va_list args);

    引数
        tag     タグ
        format  プリントする書式
        args    可変長リスト


□ vargs_sample.c
---
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h> // (1)

void debugPrint(const char* tag, const char* format, ...);  // (2)
void print(const char* tag, const char* format, va_list args);  // (3)

int main(void) {
    printf("Start¥n");

    debugPrint("TEST", "[%d %d]", 123, 456);    // (4)

    printf("End¥n");

    return EXIT_SUCCESS;
}

void debugPrint(const char* tag, const char* format, ...) {
#ifndef NDEBUG      // (5)
    va_list args;   // (6)
    va_start(args, format); // (7)
    print(tag, format, args);   // (8)
    va_end(args);   // (9)
#endif
}

void print(const char* tag, const char* format, va_list args) {
    printf("%s ", tag);
    vprintf(format, args);  // (10)
    putchar('¥n');
    fflush(stdout);
}
---

□ 実行結果
---
Start
TEST [123 456]
End
---


(1) #include <stdarg.h>

可変長引数を持った関数を宣言するために必要なヘッダーファイル。


(2) void debugPrint(const char* tag, const char* format, ...);

可変長引数を持った関数のプロトタイプ宣言。
少なくとも、ひとつは引数が必要になる。
だいたいは、この引数に続く可変長部分の形式や数をあらわすものになる。

この使い方は、標準ライブラリー関数の printf() と同じ。
ただし、デバッグ用として、改行が入り、出力した後に flush する。
また、マクロ名 NDEBUG が定義されているときは何も表示しない。


(3) void print(const char* tag, const char* format, va_list args);

可変長の引数をあらわすリスト va_list 型の引数を受け取る関数のプロトタイプ宣言。
この関数は、可変長引数部分を
    const char* format, ...
としてしまうと、実行結果は正しくなくなるので注意。


(4) debugPrint("TEST", "[%d %d]", 123, 456);

可変長の引数を持った関数の呼び出し。
ここでは、デバッグ用のプリント関数 debugPrint() を使っている。


(5) #ifndef NDEBUG

デバッグ用のプリント関数なので、マクロ名 NDEBUG が定義されているときは何も行わな
いようにする。


(6) va_list args;

可変長引数部分をあらわす (可変長リスト) 型 va_list の変数 args を定義する。


(7) va_start(args, format);

可変長リストを初期化する。
最初の引数は、va_list 型の変数。ここに、可変長リストが入る。
2 番目の引数は、可変長引数の直前の引数名。


(8) print(tag, format, args);

可変長リストを引数としてもつ関数の呼び出し。
普通の関数の呼び出しと一緒。


(9) va_end(args);

可変長リストの処理を終了する。


(10) vprintf(format, args);

可変長リストを持った標準ライブラリー関数 printf 関数の呼び出し。
この部分を、
    printf(format, args);
とすると、実行結果は正しくないので注意が必要。


                                                                            以上



CUnit: C 用 単体テスト・フレームワーク

2012年11月30日 | C / C++
                                                                    2012-11-30
CUnit: C 用 単体テスト・フレームワーク
================================================================================

■ Cunit

Cunit は、 C プログラムの単体テストをするためのフレームワークです。
このドキュメントでは、単純な使い方だけを示します。


ダウンロードとインストール

つぎからダウンロードします
CUnit
http://sourceforge.net/projects/cunit/

任意のフォルダーに展開すればインストール完了です。
このドキュメントでは、
    C:¥CUnit-2.1-0
にインストールしたことにします。


■ サンプル

サンプルとして、指定された西暦年に対して、うるう年か平年かを検査する関数 
isLeapYear() のモジュール LeapYear.c の単体テストを考えます。


□ LeapYear.h
---
/*
 * LeapYear.h
 *
 *  Created on: 2012/08/15
 *      Author: maruno
 */

#ifndef LEAPYEAR_H_
#define LEAPYEAR_H_

/**
 * 西暦 year 年がうるう年かどうか判定する。
 * @param year 西暦年
 * @return うるう年のとき1、平年のとき0
 */
int isLeapYear(int year);


#endif /* LEAPYEAR_H_ */
---


□ LeapYear.c
---
#include "LeapYear.h"

int isLeapYear(int year) {
    if ((year % 4) == 0) {
        if ((year % 100) == 0) {
            return 0;
        }
        if ((year % 400) == 0) {
            return 1;
        }
        return 1;
    }
    return 0;
}
---


■ Cunit の使用法

1. テスト・ドライバーの作成

まず、単体テストを行いたいモジュールのテスト・ドライバーを作ります。

□ LeapYearTest.c
---
#include <stdlib.h>
#include <CUnit.h>
#include <Automated.h>
#include <Basic.h>

#include "LeapYear.h"

void testIsLeapYear001(void);
void testIsLeapYear002(void);
void testIsLeapYear003(void);
void testIsLeapYear004(void);

int main(void) {
    CU_pSuite suite;

    CU_initialize_registry();
    suite = CU_add_suite("isLeapYear", NULL, NULL );
    CU_ADD_TEST(suite, testIsLeapYear001);
    CU_ADD_TEST(suite, testIsLeapYear002);
    CU_ADD_TEST(suite, testIsLeapYear003);
    CU_ADD_TEST(suite, testIsLeapYear004);
    CU_automated_run_tests();            // 結果をXML形式で出力
    CU_basic_set_mode(CU_BRM_NORMAL);    // 結果表示を失敗と実行サマリーにする
    CU_basic_run_tests();    // 結果を標準出力に出力
    CU_cleanup_registry();

    return 0;
}

/**
 * 400の倍数でうるう年
 */
void testIsLeapYear001(void) {
    CU_ASSERT_EQUAL(1, isLeapYear(2000));    // 2つの引数が等しければOK
}

/**
 * 4の倍数でうるう年
 */
void testIsLeapYear002(void) {
    CU_ASSERT_TRUE(isLeapYear(2012));    // 結果がtrueであればOK
}

/**
 * 100の倍数で平年
 */
void testIsLeapYear003(void) {
    CU_ASSERT_FALSE(isLeapYear(2100));    // 結果がfalseであればOK
}

/**
 * 4で割り切れず平年
 */
void testIsLeapYear004(void) {
    CU_ASSERT(0 == isLeapYear(2013));    // 条件式の結果がtrueであればOK
}
---

>gcc -IC:¥CUnit-2.1-0¥include¥CUnit -Wall -c LeapYearTest.c
>gcc -c -Wall LeapYear.c
>gcc -LC:¥CUnit-2.1-0¥lib -o test.exe leapYear.o LeapYearTest.o -lcunit


2. ビルドと実行

ビルド
---
>gcc -IC:¥CUnit-2.1-0¥include¥CUnit -Wall -c LeapYearTest.c
>gcc -c -Wall LeapYear.c
>gcc -LC:¥CUnit-2.1-0¥lib -o test.exe leapYear.o LeapYearTest.o -lcunit
---

※ CUnit を C:¥CUnit-2.1-0 にインストールしています。


実行

実行すると、標準出力に実行結果が出ます。
これとあわせて、つぎの結果ファイルができます。
    CUnitAutomated-Results.xml 
同じフォルダに、C:¥CUnit-2.1-0¥share¥CUnit にある
    CUnit-Run.dtd
    CUnit-Run.xsl
をコピーして、CUnitAutomated-Results.xml をダブルクリックすると、IE が立ち上がり、
グラフィカルに単体テストの結果を見ることができます。


□ 実行結果
---
>test

     CUnit - A Unit testing framework for C - Version 2.1-0
     http://cunit.sourceforge.net/


Suite isLeapYear, Test testIsLeapYear001 had failures:
    1. LeapYearTest.c:35  - CU_ASSERT_EQUAL(1,isLeapYear(2000))

--Run Summary: Type      Total     Ran  Passed  Failed
               suites        1       1     n/a       0
               tests         4       4       3       1
               asserts       4       4       3       1
---



なお、すべてパスするように修正した後の実効結果は以下のようになります。
---
     CUnit - A Unit testing framework for C - Version 2.1-0
     http://cunit.sourceforge.net/

--Run Summary: Type      Total     Ran  Passed  Failed
               suites        1       1     n/a       0
               tests         4       4       4       0
               asserts       4       4       4       0
---


■ ASSERT マクロ

代表的なマクロとして、以下のようなマクロが用意されています。

    CU_ASSERT(式)
    CU_ASSERT_TRUE(式)
    CU_ASSERT_FALSE(式)
    CU_ASSERT_EQUAL(式1, 式2)
    CU_ASSERT_NOT_EQUAL(式1, 式2)
    CU_ASSERT_PTR_EQUAL(ポインタ1, ポインタ2)
    CU_ASSERT_PTR_NOT_EQUAL(ポインタ1, ポインタ2)
    CU_ASSERT_PTR_NULL(ポインタ)
    CU_ASSERT_PTR_NOT_NULL(ポインタ)
    CU_ASSERT_STRING_EQUAL(文字列1, 文字列2)
    CU_ASSERT_STRING_NOT_EQUAL(文字列1, 文字列2)
    CU_ASSERT_NSTRING_EQUAL(文字列1, 文字列2, 文字数)
    CU_ASSERT_NSTRING_NOT_EQUAL(文字列1, 文字列2, 文字数)
    CU_ASSERT_DOUBLE_EQUAL(実数1, 実数2, 精度)
    CU_ASSERT_DOUBLE_NOT_EQUAL(実数1, 実数2, 精度)
    CU_PASS(メッセージ文字列)
    CU_FAIL(メッセージ文字列)


■ 参考

CUnit
http://sourceforge.net/projects/cunit/

CUnit チュートリアル
http://homepage3.nifty.com/kaku-chan/cunit/index.html

                                                                            以上



[C][アルゴリズム] チューリングマシンのエミュレーター

2012年06月23日 | C / C++
[C][アルゴリズム] チューリングマシンのエミュレーター
================================================================================

2ヶ月くらい前にチューリングマシンのエミュレーターを作った。
まだ、公開するつもりではなかった(もう少し検証する予定だった)が、本日が、チュー
リングの生誕100年だったので、ベータ版として公開。
そのうち、直すかもしれない。


■チューリングマシン

チューリングマシンは、テープ、ヘッド、制御部の 3 つから構成されています。

テープは無限の長さを持っていて、各ます目には 0 か 1 のどちらかの記号が書けるよう
になっています。ヘッドはテープ上の記号を読み書きするもので、常にひとつのます目を
見ています。また、制御部は、現在の状態を表す変数と動作を決める規則(プログラム)
が入っています。 制御部は、あらかじめ決められた p0 から pn までの有限個の状態の
うちのひとつを取ります(p0 を初期状態、pn を停止状態といいます)。また、このチ
ューリングマシンは、動作を決めた規則の中で、ヘッドの読んだ記号と現在の状態の 2 
つから次の動作を繰り返します。

●チューリングマシンの動作
① テープに 0 か 1 の記号を書き込む。
② ヘッドを右か左のどちらかに移動する。
③ 制御部の状態を別の(または同じ)状態に移す。

●チューリングマシンを使った計算手順
① テープ上に、入力データに相当する 0,1 の記号列を用意する。
② 制御部の状態を初期状態にして、規則に従い動作を開始する。
③ 制御部の状態が停止状態になったとき、計算を終了したものとする。
 出力(計算結果)は、停止したときのテープ上に残された記号で表される。
③’制御部の状態が停止状態にならないとき、計算は終了しないものとする。

●例
5 + 3 = 8 を行う

テープ上のデータについては、簡単にするために記号 1 の個数で整数値を表します。特
にここでは、n + 1 個の記号 1 で整数の n を表すことにし、足される 2 数の区切りと
してはひとつの記号 0 を使うものとします。

                        5      3
 入力データ:……000001111110111100000……

 このテープ上のデータに対して、次の 4 つの状態と規則でチューリングマシンを動作さ
せると出力はどうなるでしょうか。
(状態と規則)
① 制御部の状態:p0,p1,p2,p3 の 4 つ
         p0 が初期状態、p3 が停止状態

② 規則

状態 入力記号 書込み記号 ヘッドの移動 次の状態
p0     0          0          右         p0
p0     1          0          右         p1
p1     0          0          右         p3
p1     1          0          右         p2
p2     0          1          右         p3
p2     1          1          右         p2

参考)コンピュータシステムの基礎、アイテック情報技術教育研究所 (著) より


■ 実装

上記の説明に合わせて、つぎのように作る。

ルールとテープを標準入力またはファイルから指定して、動かす。
テープの動きの結果は標準出力に出る。

注)ルールはファイルを使わなければならない。

コマンド形式
---
    turing ルール・ファイル名 [テープ・ファイル名]
---


□ テープ

0 と 1 を記述したファイル。


□ ルール

つぎの 5 つをタブ区切りのファイルとして作る。
なお、停止状態は、「次の状態」の中での最大値とする。
---
状態 入力記号 書込み記号 ヘッドの移動 次の状態
---

状態            現在の状態。0 以上の整数で指定。

入力記号        現在の状態で、ヘッドが指す記号。0 または 1

書込み記号        現在の状態で、ヘッドの位置に書き込む記号。0 または 1

ヘッドの移動    ヘッドの移動方向。左は -1、右は 1

次の状態        遷移後の状態。0 以上の整数で指定。最大値が停止状態になる


■ プログラム

□ turing.c
---
/*
 ============================================================================
 Name        : turing.c
 Author      : marunomaruno
 Version     : 0.1, 2012-06-23
 Description : チューリングマシン
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

/**
 * ルール
 */
typedef struct {
    int status; // 状態(0以上の値)
    char inSymbol; // 入力記号('0', '1' または 'B')
    char outSymbol; // 書込み記号('0' または '1')
    int direction; // ヘッドの移動方向(-1: 左、1: 右)
    int nextStatus; // 次の状態(0以上の値)
} Rule;

Rule* findRule(char symbol, int status, Rule* ruleTable, int size);
void printlnRule(Rule* rule);
void printRule(Rule* rule);
int findCharOnTape(char* tape, int start, char c);
void printProgress(char* tape, int head, Rule* rule);
void printlnProgress(char* tape, int head, Rule* rule);
void printCount(char* tape);
void printlnCount(char* tape);
int readTape(FILE* in, char* tape);
int readRule(FILE* in, Rule** rule);
void printRuleTable(Rule* rule, int size);
int getStopStatus(Rule* rule, int size);

/**
 * ルールとテープをファイルから入力して、チューリングマシンを実行する。
 * ルールは、次の5項目をタブ区切りで記述する。
 *     状態, 入力記号, 書込み記号, ヘッドの移動, 次の状態
 *
 * @param argc コマンドライン・パラメーター数
 * @param argv コマンドライン・パラメーター値
 *             argv[1]: ルールのファイル。省略時は標準入力
 *             argv[2]: テープのファイル。省略時は標準入力
 * @return 常に0
 */
int main(int argc, char* argv[]) {
    printf("program start¥n");

    const int START_STATUS = 0; // 初期状態

    // 状態, 入力記号, 書込み記号, ヘッドの移動, 次の状態
    Rule* ruleTable;

    int status; // 状態

    char tape[BUFSIZ]; // テープ
    int head; // テープのヘッドの位置

    // ファイル
    FILE* tapeFp;
    FILE* ruleFp;

    switch(argc) {
    case 1:
        ruleFp = stdin;
        tapeFp = stdin;
        break;

    case 2:
        ruleFp = fopen(argv[1], "r");
        tapeFp = stdin;
        break;

    default:
        ruleFp = fopen(argv[1], "r");
        tapeFp = fopen(argv[2], "r");
        break;
    }

    if (ruleFp == NULL) {
        assert(0);
    }
    if (tapeFp == NULL) {
        assert(0);
    }

    // ルールの読み取り
    int ruleSize = readRule(ruleFp, &ruleTable);
    printf("ruleSize = %d¥n", ruleSize);
    printRuleTable(ruleTable, ruleSize);
    fclose(ruleFp);

    // テープの読み取り
    int tapeLength = readTape(tapeFp, tape);;
    printf("tape = ¥"%s¥"¥n", tape);
    fclose(tapeFp);

    // 状態を定義する
    status = START_STATUS; // 初期状態
    int stopStatus = getStopStatus(ruleTable, ruleSize);    // 停止状態

    head = 0;    // テープ・ヘッドの初期化
    while (status != stopStatus) {
        assert(0 <= head && head < tapeLength);
        Rule* rule = findRule(tape[head], status, ruleTable, ruleSize); 
                                                             // ルールを見つける
        printlnProgress(tape, head, rule);
        tape[head] = rule->outSymbol;    // テープの書き換え
        head += rule->direction;    // ヘッドを移動
        status = rule->nextStatus;    // 状態を新しい状態にする
    }
    printlnProgress(tape, head, NULL);

    free(ruleTable);

    printf("program end¥n");
    return EXIT_SUCCESS;
}

/**
 * テープを読み込む。
 * テープの最大長は、BUFSIZ (512) バイトとする。
 * @param in ファイルポインター
 * @param tape テープ
 * @return テープの長さ
 */
int readTape(FILE* in, char* tape) {
    assert(in != NULL);
    assert(tape != NULL);

    fgets(tape, BUFSIZ, in);
    tape[BUFSIZ] = '¥0';
    char* p = strstr(tape, "¥n");
    if (p != NULL) {
        *p = '¥0';
    }
    return strlen(tape);
}

/**
 * ルールを読み込む。
 * なお、ルール配列はこの関数中で領域確保するので、使用側で解放する必要がある。
 * @param in ファイルポインター
 * @param rule ルール配列
 * @return ルール配列のサイズ
 */
int readRule(FILE* in, Rule** rule) {
    assert(in != NULL);
    assert(rule != NULL);

    const int RULE_MALLOC_UNIT = 16;   // ルール配列をとる場合のサイズの初期単位
    const char* DELIMITTER = "¥t";
    int limit = RULE_MALLOC_UNIT;
    int size = 0;
    char buf[BUFSIZ];
    char token[BUFSIZ];

    Rule* p = (Rule*) malloc(RULE_MALLOC_UNIT * sizeof(Rule));
    assert(p != NULL);
    *rule = p;

    size = 0;
    while (fgets(buf, BUFSIZ, in) != NULL) {
        size++;
        if (size == limit) {
            limit *= 2;
            realloc(*rule, limit * sizeof(Rule));
            assert(*rule != NULL);
            p = *rule + size - 1;
        }

        strncpy(token, strtok(buf, DELIMITTER), BUFSIZ);
        token[BUFSIZ] = '¥0';
        p->status = atoi(token);

        strncpy(token, strtok(NULL, DELIMITTER), BUFSIZ);
        token[BUFSIZ] = '¥0';
        p->inSymbol = token[0];

        strncpy(token, strtok(NULL, DELIMITTER), BUFSIZ);
        token[BUFSIZ] = '¥0';
        p->outSymbol = token[0];

        strncpy(token, strtok(NULL, DELIMITTER), BUFSIZ);
        token[BUFSIZ] = '¥0';
        p->direction = atoi(token);

        strncpy(token, strtok(NULL, DELIMITTER), BUFSIZ);
        token[BUFSIZ] = '¥0';
        p->nextStatus = atoi(token);

        p++;
    }

    return size;
}

/**
 * 途中経過をプリントする。
 * @param tape テープ
 * @param head ヘッドの位置
 * @param rule ルール
 */
void printProgress(char* tape, int head, Rule* rule) {
    assert(tape != NULL);
    assert(0 <= head && head < BUFSIZ);

    int i;

    printf("%s ", tape);
    printRule(rule);
    printf(" ");

    printlnCount(tape);

    for (i = 0; i < head; i++) {
        printf(" ");
    }
    printf("^");
    fflush(stdout);
}

/**
 * 途中経過をプリントし、改行する。
 * @param tape テープ
 * @param head ヘッドの位置
 * @param rule ルール
 */
void printlnProgress(char* tape, int head, Rule* rule) {
    assert(tape != NULL);
    assert(0 <= head && head < BUFSIZ);

    printProgress(tape, head, rule);
    printf("¥n");
    fflush(stdout);
}

/**
 * 停止状態を取得する。
 * 停止状態は、ルール中の状態の最大値になる。
 * @param rule ルール
 * @param size ルール配列のサイズ
 * @return 停止状態
 */
int getStopStatus(Rule* rule, int size) {
    assert(rule != NULL);
    assert(size > 0);

    int max = rule->nextStatus;
    rule++;
    int i = 0;
    for (i = 1; i < size; i++) {
        if (max < rule->nextStatus) {
            max = rule->nextStatus;
        }
        rule++;
    }
    return max;
}

/**
 * ルールをプリントする。
 * @param rule ルール
 * @param size ルール配列のサイズ
 */
void printRuleTable(Rule* rule, int size) {
    assert(rule != NULL);
    assert(size > 0);

    int i = 0;
    for (i = 0; i < size; i++) {
        printlnRule(rule);
        rule++;
    }
    fflush(stdout);
}

/**
 * ルールをプリントする。
 * @param rule ルール. NULLのときは、空白をプリントする。
 */
void printRule(Rule* rule) {
    if (rule != NULL) {
        printf("[%d, %c, %c, %d, %d]", rule->status, rule->inSymbol,
                rule->outSymbol, rule->direction, rule->nextStatus);
    } else {
        printf("[      -      ]");
    }

    fflush(stdout);
}

/**
 * ルールをプリントし、改行する。
 * @param rule ルール
 */
void printlnRule(Rule* rule) {
    printRule(rule);
    printf("¥n");
    fflush(stdout);
}

/**
 * 記号と状態から合致するルールを見つける
 * @param symbol 記号
 * @param status 状態
 * @param ruleTable ルール表
 * @param size ruleTableの要素数
 * @return 合致したルール
 */
Rule* findRule(char symbol, int status, Rule* ruleTable, int size) {
    assert(symbol == '0' || symbol == '1');
    assert(status >= 0);
    assert(ruleTable != NULL);
    assert(size > 0);

//    printf("* %c %d %d¥n", symbol, status, size); fflush(stdout);

    Rule* rule = NULL;
    for (rule = ruleTable; rule < ruleTable + size; rule++) {
        assert(ruleTable <= rule && rule < ruleTable + size);
        if (symbol == rule->inSymbol && status == rule->status) {
            return rule;
        }
    }

    assert(0);    // 必ず見つかるはず
    return NULL;
}

/**
 * startの位置から、指定された文字cを見つけ、その位置を返す。
 * @param tape テープ
 * @param start 見つけるための開始位置
 * @param c 見つける文字
 * @return 見つかった位置。見つからない場合は-1
 */
int findCharOnTape(char* tape, int start, char c) {
    assert(tape != NULL);
    assert(start >= 0);
    assert(c == '0' || c == '1');

    int i = -1;

    for (i = start; i < strlen(tape); i++) {
        assert(start <= i && i < strlen(tape));
        if (tape[i] == c) {
            return i;
        }
    }

    return -1;
}

/**
 * テープにある連続した1の数をプリントする。
 * @param tape テープ
 */
void printCount(char* tape) {
    assert(tape != NULL);

    int i = -1;
    int start = -1;

    // 1の位置を検索する
    while ((i = findCharOnTape(tape, (i + 1), '1')) != -1) {
        // 次の0の位置
        start = i;
        if ((i = findCharOnTape(tape, (start + 1), '0')) == -1) {
            printf("%d ", (strlen(tape) - start - 1));
            break;
        }

        printf("%d ", (i - start - 1));
    }
    fflush(stdout);
}

/**
 * テープにある連続した1の数をプリントし、改行する。
 * @param tape テープ
 */
void printlnCount(char* tape) {
    assert(tape != NULL);

    printCount(tape);
    printf("¥n");
    fflush(stdout);
}
---


■ 実行


例にあるように、「5 + 3 = 8」を計算する。


□ tape.txt
---
000001111110111100000
---

1 の並んでいる数(から 1 引いたもの)が数値をあらわす。
間の 0 が区切り。


□ rule.txt
---
0    0    0    1    0
0    1    0    1    1
1    0    0    1    3
1    1    0    1    2
2    0    1    1    3
2    1    1    1    2
---

最大の状態が「3」。


□ 実行結果
---
program start
ruleSize = 6
[0, 0, 0, 1, 0]
[0, 1, 0, 1, 1]
[1, 0, 0, 1, 3]
[1, 1, 0, 1, 2]
[2, 0, 1, 1, 3]
[2, 1, 1, 1, 2]
tape = "000001111110111100000"
000001111110111100000 [0, 0, 0, 1, 0] 5 3 
^
000001111110111100000 [0, 0, 0, 1, 0] 5 3 
 ^
000001111110111100000 [0, 0, 0, 1, 0] 5 3 
  ^
000001111110111100000 [0, 0, 0, 1, 0] 5 3 
   ^
000001111110111100000 [0, 0, 0, 1, 0] 5 3 
    ^
000001111110111100000 [0, 1, 0, 1, 1] 5 3 
     ^
000000111110111100000 [1, 1, 0, 1, 2] 4 3 
      ^
000000011110111100000 [2, 1, 1, 1, 2] 3 3 
       ^
000000011110111100000 [2, 1, 1, 1, 2] 3 3 
        ^
000000011110111100000 [2, 1, 1, 1, 2] 3 3 
         ^
000000011110111100000 [2, 1, 1, 1, 2] 3 3 
          ^
000000011110111100000 [2, 0, 1, 1, 3] 3 3 
           ^
000000011111111100000 [      -      ] 8 
            ^
program end
---

                                                                            以上



[C][SJIS] Shift_JIS 文字関係のユーティリティ

2012年06月10日 | C / C++
[C][SJIS] Shift_JIS 文字関係のユーティリティ
================================================================================

Shift_JIS 文字関係のユーティリティとして、次のようなものを作った。
ただし、リトル・エンディアン限定。

関数の概要
---
int stoi(const char* s);              日本語の数字列を数値(int)に変換する。
int sjisToDigit(const char* s);       日本語の数字を数値(int)に変換する。
int isSjisDigit(const char* s);       数字("0"~"9")か判断する。
int isSjisHiragana(const char* s);    ひらがなか判断する。
int isSjisKatakana(const char* s);    カタカナか判断する。
---

関数の詳細
---
int stoi(const char* s);

    引数sで与えられた Shift_JIS で書かれた日本語の数字列を数値(int)に変換する。
    この関数は、標準関数 atoi() の Shift_JIS 版であるが、負値を扱えない。
    数字列は Shift_JIS の数字以外は記述できない。Shift_JIS の数字以外が出てきた
    時点で変換をやめる。

    引数    s  Shift_JIS で書かれた日本語の数字列

    戻り値  Shift_JIS で書かれた日本語の数字列を変換した数値


int sjisToDigit(const char* s);

    引数sで与えられた Shift_JIS で書かれた日本語の数字を数値(int)に変換する。
    この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだ
    けで判断する。

    引数    s  Shift_JIS で書かれた日本語の数字

    戻り値  Shift_JIS で書かれた日本語の数字を変換した数値。変換できない場合は -1


int isSjisDigit(const char* s);

    引数sで与えられた Shift_JIS で書かれた日本語が数字("0"~"9")か判断する。
    この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだ
    けで判断する。
     Shift_JIS の"0"~"9"は、0x824f ~ 0x8258.

    引数   s  Shift_JIS で書かれた日本語の数字

    戻り値 数値であれば 1.そうでなければ 0


int isSjisHiragana(const char* s);

    引数sで与えられた Shift_JIS で書かれた日本語がひらがなか判断する。
    この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだ
	けで判断する。
    Shift_JIS のひらがなは、0x829F ~ 0x82F1.

    引数   s  Shift_JIS で書かれた日本語文字列

    戻り値 ひらがなであれば 1.そうでなければ 0


int isSjisKatakana(const char* s);

    引数 s で与えられた Shift_JIS で書かれた日本語がカタカナか判断する。
    この数字は、C言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだ
	けで判断する。
    Shift_JIS のカタカナは、0x829F ~ 0x82F1.

    引数   s  Shift_JIS で書かれた日本語文字列

    戻り値 カタカナであれば 1.そうでなければ 0
---



▲ 公開用ヘッダー

□ sjisUtil.h
---
/*
 ============================================================================
 Name        : sjisUtil.h
 Author      : marunomaruno
 Version     : V1.0, 2012-06-08
 Copyright   : marunomaruno
 Description : リトルエンエンディアンの Shift_JIS 関係のユーティリティ(公開用)
 ============================================================================
 */

#ifndef SJIS_UTIL_H_
#define SJIS_UTIL_H_

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語がひらがなか判断する。
 * この数字は、C言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * Shift_JIS のひらがなは、0x829F ~ 0x82F1.
 * @param s  Shift_JIS で書かれた日本語文字列
 * @return ひらがなであれば 1.そうでなければ 0
 */
int isSjisHiragana(const char* s);

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語がカタカナか判断する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * Shift_JIS のカタカナは、0x829F ~ 0x82F1.
 * @param s  Shift_JIS で書かれた日本語文字列
 * @return カタカナであれば 1.そうでなければ 0
 */
int isSjisKatakana(const char* s);

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語の数字列を数値(int)に変換する。
 * この関数は、標準関数 atoi() の Shift_JIS 版であるが、負値を扱えない。
 * 数字列は Shift_JIS の数字以外は記述できない。 Shift_JIS の数字以外が出てきた時点で変換をやめる。
 * @param s  Shift_JIS で書かれた日本語の数字列
 * @return  Shift_JIS で書かれた日本語の数字列を変換した数値
 */
int stoi(const char* s);

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語の数字を数値(int)に変換する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * @param s  Shift_JIS で書かれた日本語の数字
 * @return  Shift_JIS で書かれた日本語の数字を変換した数値。変換できない場合は-1
 */
int sjisToDigit(const char* s);

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語が数字("0"~"9")か判断する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * Shift_JIS の"0"~"9"は、0x824f ~ 0x8258.
 * @param s  Shift_JIS で書かれた日本語の数字
 * @return 数値であれば 1.そうでなければ 0
 */
int isSjisDigit(const char* s);

#endif /* SJIS_UTIL_H_ */
----


▲ 非公開用ヘッダー

内部的に使うだけの関数の定義。
リトルエンディアンを意識するのはここだけ。

□ sjisUtilInner.h
---
/*
 ============================================================================
 Name        : sjisUtilInner.h
 Author      : marunomaruno
 Version     : V1.0, 2012-06-08
 Copyright   : marunomaruno
 Description : リトルエンエンディアンの Shift_JIS 関係のユーティリティ(内部用)
 ============================================================================
 */

#ifndef SJIS_UTIL_INNER_H_
#define SJIS_UTIL_INNER_H_

/**
 * 2 バイト文字 s が 2 バイト文字 start と end の間に入っているか判断する。
 * この 2 バイト文字は、C 言語の文字列の要件を満たす必要はなく、それぞれの引数からの 2 バイトだけで判断する。
 * @param s  Shift_JIS で書かれた 2 バイト文字
 * @param start  Shift_JIS で書かれた 2 バイト文字で、検査する範囲の始端
 * @param end  Shift_JIS で書かれた 2 バイト文字で、検査する範囲の終端
 * @return 範囲内であれば 1.そうでなければ 0
 */
static int inRange2byteChar(const char* s, const char* start, const char* end);

#endif /* SJIS_UTIL_INNER_H_ */
---


▲ モジュール

□ sjisUtil.c
---
/*
 ============================================================================
 Name        : sjisUtil.c
 Author      : marunomaruno
 Version     : V1.0, 2012-06-08
 Copyright   : marunomaruno
 Description : リトルエンエンディアンの Shift_JIS 関係のユーティリティ
 ============================================================================
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sjisUtil.h"
#include "sjisUtilInner.h"

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語の数字列を数値(int)に変換する。
 * この関数は、標準関数 atoi() の Shift_JIS 版であるが、負値を扱えない。
 * 数字列は Shift_JIS の数字以外は記述できない。 Shift_JIS の数字以外が出てきた時点で変換をやめる。
 * @param s  Shift_JIS で書かれた日本語の数字列
 * @return  Shift_JIS で書かれた日本語の数字列を変換した数値
 */
int stoi(const char* s) {
    int num = 0;
    int i;

    for (i = 0; i < strlen(s); i += 2) {
        int digit = 0;
        if ((digit = sjisToDigit(&s[i])) == -1) {
            break;
        }
        num = num * 10 + digit;
    }

    return num;
}

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語の数字を数値(int)に変換する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * @param s  Shift_JIS で書かれた日本語の数字
 * @return  Shift_JIS で書かれた日本語の数字を変換した数値。変換できない場合は -1
 */
int sjisToDigit(const char* s) {
    if (!isSjisDigit(s)) {
        return -1;
    }

    return s[1] - 0x4f;
}

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語が数字("0"~"9")か判断する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * Shift_JIS の"0"~"9"は、0x824f ~ 0x8258.
 * @param s  Shift_JIS で書かれた日本語の数字
 * @return 数値であれば 1.そうでなければ 0
 */
int isSjisDigit(const char* s) {
    return inRange2byteChar(s, "0", "9");
}

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語がひらがなか判断する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * Shift_JIS のひらがなは、0x829F ~ 0x82F1. 長音(ー)もひらがなとする。
 * @param s  Shift_JIS で書かれた日本語文字列
 * @return ひらがなであれば 1.そうでなければ 0
 */
int isSjisHiragana(const char* s) {
    return inRange2byteChar(s, "ぁ", "ん") || inRange2byteChar(s, "ー", "ー");
}

/**
 * 引数 s で与えられた Shift_JIS で書かれた日本語がカタカナか判断する。
 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。
 * Shift_JIS のカタカナは、0x829F ~ 0x82F1. 長音(ー)もカタカナとする。
 * @param s  Shift_JIS で書かれた日本語文字列
 * @return カタカナであれば 1.そうでなければ 0
 */
int isSjisKatakana(const char* s) {
    return inRange2byteChar(s, "ァ", "ヶ") || inRange2byteChar(s, "ー", "ー");
}

/**
 * 2 バイト文字sが 2 バイト文字startとendの間に入っているか判断する。
 * この 2 バイト文字は、C 言語の文字列の要件を満たす必要はなく、それぞれの引数からの 2 バイトだけで判断する。
 * @param s  Shift_JIS で書かれた 2 バイト文字
 * @param start  Shift_JIS で書かれた 2 バイト文字で、検査する範囲の始端
 * @param end  Shift_JIS で書かれた 2 バイト文字で、検査する範囲の終端
 * @return 範囲内であれば 1.そうでなければ 0
 */
int inRange2byteChar(const char* s, const char* start, const char* end) {
    typedef union {
        unsigned char str[2];
        unsigned short num;
    } DoubleByteChar;

    DoubleByteChar value;
    DoubleByteChar startValue;
    DoubleByteChar endValue;

    value.str[0] = s[1];
    value.str[1] = s[0];

    startValue.str[0] = start[1];
    startValue.str[1] = start[0];

    endValue.str[0] = end[1];
    endValue.str[1] = end[0];

    return (startValue.num <= value.num) && (value.num <= endValue.num);
}
---


▲ テスト

上記の関数を確認するためのテスト・プログラム。
数字、ひらがな、かたかなの限界値をチェックする。
また、リトルエンディアン前提なので、バイト順が逆転している場合に範囲になるかどう
かもチェックする。


□ test.c
---
/*
 ============================================================================
 Name        : test.c
 Author      : marunomaruno
 Version     : V1.0, 2012-06-08
 Copyright   : marunomaruno
 Description : リトルエンエンディアンの Shift_JIS 関係のユーティリティのテスト・
プログラム
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "sjisUtil.h"

int main(void) {
    printf("test start\n");

    char hiragana[][3] = { "あ", "い", "か", "さ", "た", "や", "ら", "わ", "っ",
 "ん",
            "ー", };
    char katakana[][3] = { "ア", "イ", "ウ", "サ", "タ", "ヤ", "ラ", "ワ", "ッ",
 "ン",
            "ー", };
    char num[][3] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
 };
    char other[][3] = { { 0x4e, 0x82, '0' }, { 0x59, 0x82, '0' }, { 0x9e, 0x82,
            '0' }, { 0xf2, 0x82, '0' }, { 0xfc, 0x82, '0' },
            { 0x97, 0x823, '0' }, "氓", "A", "一", "所", };
    int i;

    // 長音も含め、ひらがな、カタカナ、数字
    for (i = 0; i < 11; i++) {
        assert(isSjisHiragana(hiragana[i]));
    }

    for (i = 0; i < 11; i++) {
        assert(isSjisKatakana(katakana[i]));
    }

    for (i = 0; i < 10; i++) {
        assert(isSjisDigit(num[i]));
    }

    // 長音
    assert(isSjisKatakana("ー"));
    assert(isSjisHiragana("ー"));

    // ひらがな、カタカナ、数字でない
    for (i = 0; i < 10; i++) {
        assert(!isSjisHiragana(katakana[i]));
        assert(!isSjisHiragana(num[i]));
        assert(!isSjisHiragana(other[i]));
    }

    for (i = 0; i < 10; i++) {
        assert(!isSjisKatakana(hiragana[i]));
        assert(!isSjisKatakana(num[i]));
        assert(!isSjisKatakana(other[i]));
    }

    for (i = 0; i < 10; i++) {
        assert(!isSjisDigit(hiragana[i]));
        assert(!isSjisDigit(katakana[i]));
        assert(!isSjisDigit(other[i]));
    }

    // 全角数字を数値に変換
    for (i = 0; i < 10; i++) {
        assert(i == sjisToDigit(num[i]));
    }

    // 全角数字を数値に変換
    assert(1234 == stoi("1234"));
    assert(1234567890 == stoi("1234567890"));
    assert(0 == stoi("01234567890"));
    assert(123456 == stoi("12345607890"));

    // 文字列としての検査
    char* s1;
    s1 = "あいうえおー";
    for (i = 0; i < strlen(s1); i += 2) {
        if (!isSjisHiragana(&s1[i])) {
            assert(0);
        }
    }
    assert(1);

    s1 = "あいうえおー1";
    for (i = 0; i < strlen(s1); i += 2) {
        if (!isSjisHiragana(&s1[i])) {
            assert(1);
        }
    }
    assert(i >= strlen(s1));

    s1 = "あいうえおーア";
    for (i = 0; i < strlen(s1); i += 2) {
        if (!isSjisHiragana(&s1[i])) {
            assert(1);
        }
    }
    assert(i >= strlen(s1));

    s1 = "アイウエオー";
    for (i = 0; i < strlen(s1); i += 2) {
        if (!isSjisKatakana(&s1[i])) {
            assert(0);
        }
    }
    assert(1);

    s1 = "アイウエオー1";
    for (i = 0; i < strlen(s1); i += 2) {
        if (!isSjisKatakana(&s1[i])) {
            assert(1);
        }
    }
    assert(i >= strlen(s1));

    s1 = "アイウエオーあいうえおア";
    for (i = 0; i < strlen(s1); i += 2) {
        if (!isSjisKatakana(&s1[i])) {
            assert(1);
        }
    }
    assert(i >= strlen(s1));

    printf("test end\n");
    return EXIT_SUCCESS;
}
---

                                                                            以上


[C++] テンプレートの特殊化

2011年11月19日 | C / C++
[C++] テンプレートの特殊化
================================================================================

テンプレートの指定で、Java のように、あるクラスを継承したクラスのみに限定する、
ということができる。
    Javaの例: class MyClass<T extends Number>

「テンプレートの特殊化」というものを使って実現できる。
でも、かなり面倒くさい。

まずは汎用的なテンプレート関数を宣言する。
    template<class T>                               // (4)
    void print(T& t) {

テンプレートの特殊化ということで、特定の型に対するテンプレート関数を宣言する。
    template<>                                      // (5)
    void print<Base> (Base& t) {

派生クラスのオブジェクト d を引数に渡すときは、基本クラスの型として渡す。
    print(*(dynamic_cast<Base*> (&d)));         // (10)

これで、派生クラスのオブジェクト d に対して、Base 型のテンプレート関数が使われる。

もちろん、単に
    print(d);                                   // (8)
とした場合は、汎用的なテンプレート関数が呼び出される。


□ generic_sample.cpp
---
#include <iostream>
using namespace std;

class Base {                                    // (1)
public:
    virtual void print() {
        cout << "Base" << endl;
    }
};

class Derived: public Base {                    // (2)
public:
    void print() {
        cout << "Derived" << endl;
    }
};

class Other {                                   // (3)
public:
    void print() {
        cout << "Other" << endl;
    }
};

template<class T>                               // (4)
void print(T& t) {
    cout << "G: ";
    t.print();
}

template<>                                      // (5)
void print<Base> (Base& t) {
    cout << "B: ";
    t.print();
}

//template<>                                    // (6)
//void print<Derived>(Derived& t) {
//    cout << "D: ";
//    t.print();
//}

int main() {
    Base b;
    print(b);                                   // (7)

    Derived d;
    print(d);                                   // (8)
    print(*((Base*) (&d)));                     // (9)
    print(*(dynamic_cast<Base*> (&d)));         // (10)

    Other o;
    print(o);                                   // (11)
//    print(*((Base*) (&o)));                   // (12) // 実行時エラー
//    print(*(dynamic_cast<Base*> (&o)));    // (13) // コンパイルエラー

    return 0;
}
---

□ 実行結果
---
B: Base                <- (7)
G: Derived             <- (8)
B: Derived             <- (9)
B: Derived             <- (10)
G: Other               <- (11)
---


(1) 基本クラス Base

    class Base {                                    // (1)
        ...
    };


(2) 派生クラス Derived

基本クラス Base を継承し、print() 関数をオーバーライドする。

    class Derived: public Base {                    // (2)
        ...
    };


(3) 関係ないクラス Other

Base クラスとも、Derived クラスとも関係のないクラス。

    class Other {                                   // (3)
        ...
    };


(4) テンプレート

汎用のテンプレート関数。引数は参照で受け取る。

    template<class T>                               // (4)
    void print(T& t) {
        ...
    }


(5) テンプレートの特殊化

Base 型で受け取るテンプレート関数。

    template<>                                      // (5)
    void print<Base> (Base& t) {
        ...
    }


(6) テンプレートの特殊化

Derived 型で受け取るテンプレート関数。今回は、コメントアウトしている。

    //template<>                                    // (6)
    //void print<Derived>(Derived& t) {
        ...
    //}

上記のコードを有効にしておくと、実行結果は次のようになる。

---
B: Base                <- (7) 
D: Derived             <- (8)
B: Derived             <- (9)
B: Derived             <- (10)
G: Other               <- (11)
---


(7) 基本クラスのオブジェクトをプリント

    print(b);                                   // (7)

特殊化によって、(5)のテンプレート関数が使われる。


(8)-(10) 派生クラスのオブジェクトをプリント

    print(d);                                   // (8)

特殊化は、あくまでも、そのクラス(型)が対象なので、その型の派生クラスというのは関
係ない。したがって、これは、あくまでも汎用のテンプレート関数を使う。


    print(*((Base*) (&d)));                     // (9)
    print(*(dynamic_cast<Base*> (&d)));         // (10)

派生クラスのオブジェクトを、基本クラスの型にキャストしてしまえば、基本クラスを使
った特殊テンプレートが利用できる。

キャストの方法としては、(9)のように、C言語のキャスト、dynamic_cast を使う 2 つの
方法があるが、dynamic_cast を使うほうがよい。これは、つぎの説明のように、基本ク
ラスとは派生関係にないクラスのオブジェクトをキャストして、特殊化したテンプレート
関数を使わせたいときに、C言語のキャストでは、実行時までエラーがわからないが、
dynamic_cast であれば、コンパイル・エラーになるからである。


(11) 関係ないクラスのオブジェクトをプリント

    print(o);                                   // (11)

汎用のテンプレート関数を使う。


//    print(*((Base*) (&o)));                   // (12) // 実行時エラー

このコードは、実行時にエラーになる。


//    print(*(dynamic_cast<Base*> (&o)));   // (13) // コンパイルエラー

このコードは、次のコンパイルエラーになる。
---
In function `int main()':
error: cannot dynamic_cast `&o' (of type `class Other*') to type `class Base
*' (source type is not polymorphic)
Build error occurred, build is stopped
---



[C++] ダイヤモンド継承

2011年11月03日 | C / C++
ダイヤモンド継承
================================================================================

※ mingw 3.4.2


ダイヤモンド継承で virtual 指定のときに、オーバーロードされた違うコンストラク
ターで初期化したときにどうなるの、ということで、試してみる。

クラス図としては、つぎのとおり
(U言語、http://objectclub.jp/technicaldoc/uml/u_lang/)。

    A <|------ B <|------- D
          +--- C <|---+

U 言語での多重継承の描き方はどうなんだろう。上記の表現は適当に記したが、本当はど
うかな。


□ diamond.cpp
---
#include <iostream>
using namespace std;

class A {
public:
    int a;
    A();
    A(int a);
};

class B: virtual public A {
public:
    B();
};

class C: virtual public A {
public:
    C(int a);
};

class D: public B, public C {
public:
    D(int a);
};

A::A() :
    a(-1) {
    cout << "A::A()" << endl;
}

A::A(int a) :
    a(a) {
    cout << "A::A(int a), a = " << a << endl;
}

B::B() :
    A() {
    cout << "B::B()" << endl;
}

C::C(int a) :
    A(a) {
    cout << "C::C(int a), a = " << a << endl;
}

D::D(int a) :
    B(), C(a) {
    cout << "D::D(int a), a = " << a << endl;
}

int main() {
    D d(123);
    cout << "d.a = " << d.a << endl;

    return 0;
}
---

□ 実行結果
---
A::A()
B::B()
C::C(int a), a = 123
D::D(int a), a = 123
d.a = -1
---



■ クラスD が B や C を指定する順番を逆にする


継承する順番を逆にすれば、d.a の値が 123 になるかと思ったが、そうはならず、-1 の
まま。
なぜだろう。


□ diamond.cpp
---
#include <iostream>
using namespace std;

class A {
public:
    int a;
    A();
    A(int a);
};

class B: virtual public A {
public:
    B();
};

class C: virtual public A {
public:
    C(int a);
};

//class D : public B, public C {
class D: public C, public B {
public:
    D(int a);
};

A::A() :
    a(-1) {
    cout << "A::A()" << endl;
}

A::A(int a) :
    a(a) {
    cout << "A::A(int a), a = " << a << endl;
}

B::B() :
    A() {
    cout << "B::B()" << endl;
}

C::C(int a) :
    A(a) {
    cout << "C::C(int a), a = " << a << endl;
}

//D::D(int a) :
//    B(), C(a) {
//    cout << "D::D(int a), a = " << a << endl;
//}

D::D(int a) :
    C(a), B() {
    cout << "D::D(int a), a = " << a << endl;
}

int main() {
    D d(123);
    cout << "d.a = " << d.a << endl;

    return 0;
}
---

□ 実行結果
---
A::A()
C::C(int a), a = 123
B::B()
D::D(int a), a = 123
d.a = -1
---


■ vartual を外す

vairtual を外すと、さすがにコンパイル・エラー。
    request for member `a' is ambiguous

□ diamond.cpp
---
#include <iostream>
using namespace std;

class A {
public:
    int a;
    A();
    A(int a);
};

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

class C: public A {
public:
    C(int a);
};

class D: public B, public C {
public:
    D(int a);
};

A::A() :
    a(-1) {
    cout << "A::A()" << endl;
}

A::A(int a) :
    a(a) {
    cout << "A::A(int a), a = " << a << endl;
}

B::B() :
    A() {
    cout << "B::B()" << endl;
}

C::C(int a) :
    A(a) {
    cout << "C::C(int a), a = " << a << endl;
}

D::D(int a) :
    B(), C(a) {
    cout << "D::D(int a), a = " << a << endl;
}

int main() {
    D d(123);
    cout << "d.a = " << d.a << endl;

    return 0;
}
---

□ ビルド結果
---
..\src\diamond.cpp: In function `int main()':
..\src\diamond.cpp:53: error: request for member `a' is ambiguous
..\src\diamond.cpp:6: error: candidates are: int A::a
..\src\diamond.cpp:6: error:                 int A::a
---




[C][C++] 2 次元配列を返り値に持つ関数

2011年10月28日 | C / C++
                                                                    2011-10-28
2 次元配列を返り値に持つ関数
================================================================================

サンプルコードは C++ 言語で書かれているが、C 言語でも同じ。

サンプルの関数 addTo() は、2 次元配列(正方行列)に対して、擬似コード的に記すと
    a += b;
を行う関数。


□ sample.cpp
---
#include <iostream>
#include <iomanip>
using namespace std;

int (*addTo(int a[][2], int b[][2], int size))[2];    // 2次元配列を加算する (1)
void print(int a[][2], int size);

int main() {
    int a[2][2] = {{1, 2}, {3, 4}};
    int b[2][2] = {{5, 6}, {7, 8}};
    print(a, 2);
    cout << "--------" << endl;
    print(b, 2);

    int (*c)[2] = addTo(a, b, 2);    // (2)
    cout << "========" << endl;
    print(c, 2);

    return 0;
}

int (*addTo(int a[][2], int b[][2], int size))[2] {
    for (int i = 0; i < size; ++i) {
        for (int j = 0; j < size; ++j) {
            a[i][j] += b[i][j];
        }
    }
    return a;
}

void print(int a[][2], int size) {
    for (int i = 0; i < size; ++i) {
        for (int j = 0; j < size; ++j) {
            cout << setw(4) << a[i][j];
        }
        cout << endl;
    }
}
---

□ 実行結果
---
   1   2
   3   4
--------
   5   6
   7   8
========
   6   8
  10  12
---


(1) 2 次元配列を返す関数のプロトタイプ宣言

    int (*addTo(int a[][2], int b[][2], int size))[2];    // 2次元配列を加算する
 

通常の2次元配列を指すポインター変数の定義はつぎの(2)のように、
    int (*c)[2]
のようになる。この場合のポインター c は、int[2] のサイズを持つ型を指すことになる
ので、結果として 2 次元配列へのポインターと考えられる。

こう考えると、返り値として int[2] を持つ関数としては、
    int (*x)[2]
のようになり、x 部分が関数のシグネチャー部分とするとよい。すなわち、上記のような
表現になる。


(2)    2 次元配列の宣言

    int (*c)[2] = addTo(a, b, 2);    // (2)



■ 関数ポインターにしてみる

次のようにできる。
関数名の部分を
    (*関数ポインター名)
に置き換えるだけである。
ただし、これだとなんだかよくわからなくなるので、あまり記述しないほうがよさそう。
それ以前に、2 次元配列を返す関数を作るくらいなら、構造体に 2 次元配列を入れて扱
ったほうが記述としてはよいと思う。

---
        :
    int (*(*fp)(int a[][2], int b[][2], int size))[2];    // 関数ポインターを定
義する
    fp = addTo;    // 関数ポインターにアドレスを設定する

    int (*c)[2] = fp(a, b, 2);
        :
---




[C] 可変長引数

2011年09月03日 | C / C++
                                                                2012-08-25 更新
可変長引数
================================================================================

C 言語での可変長引数を持つ関数の定義は、以下のようにする。

1. 仮引数は、最初の引数に続いて、「...」を続ける

2. 次の    va_list 型の変数を定義して、これを使う。
    va_list list;

3. 可変長引数を使う準備する
    va_start(list, 最初の仮引数);

4. 引数を取得する
    va_arg(list, 仮引数のデータ型)

5. 可変長引数の処理を終了する
    va_end(list);


□ va_func.c
---
/*
 * 可変長引数を確認するサンプル。
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

int va_func(const char* types, ...);    // (1)

int main(void) {
    va_func("idcsxi", 12, 3.14, 'A', "Hello", 56, -78);    // (2)

    return EXIT_SUCCESS;
}

/**
 * 可変長引数を受け取り、フォーマットにしたがって表示する。
 * @param types 引数のデータ型
 *                     i: int 型
 *                     d: double 型
 *                     c: char 型
 *                     s: char* 型
 *     @return 正しく表示できた引数の数
 */
int va_func(const char* types, ...) {
    const char* type;
    int count = 0;

    va_list list;    // (3)
    va_start(list, types);    // (4)

    for (type = types; *type != '
2012-08-25 更新
可変長引数
================================================================================

C 言語での可変長引数を持つ関数の定義は、以下のようにする。

1. 仮引数は、最初の引数に続いて、「...」を続ける

2. 次の va_list 型の変数を定義して、これを使う。
va_list list;

3. 可変長引数を使う準備する
va_start(list, 最初の仮引数);

4. 引数を取得する
va_arg(list, 仮引数のデータ型)

5. 可変長引数の処理を終了する
va_end(list);


□ va_func.c
---
/*
* 可変長引数を確認するサンプル。
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

int va_func(const char* types, ...); // (1)

int main(void) {
va_func("idcsxi", 12, 3.14, 'A', "Hello", 56, -78); // (2)

return EXIT_SUCCESS;
}

/**
* 可変長引数を受け取り、フォーマットにしたがって表示する。
* @param types 引数のデータ型
* i: int 型
* d: double 型
* c: char 型
* s: char* 型
* @return 正しく表示できた引数の数
*/
int va_func(const char* types, ...) {
const char* type;
int count = 0;

va_list list; // (3)
va_start(list, types); // (4)

for (type = types; *type != '\0'; type++) {
switch (*type) {
case 'i':
printf(" int: %d\n", va_arg(list, int)); // (5)
count++;
break;

case 'd':
printf("double: %f\n", va_arg(list, double)); // (5)
count++;
break;

case 'c':
printf(" char: %c\n", (char) va_arg(list, int)); // (5)
count++;
break;

case 's':
printf("string: %s\n", va_arg(list, char*)); // (5)
count++;
break;

default:
printf("(bad type)\n");
va_arg(list, void*); // 空読みする // (5)
break;
}
}

va_end(list); // (6)

return count;
}
---

□ 実行結果
---
int: 12
double: 3.140000
char: A
string: Hello
(bad type)
int: -78
---

(1) 可変長引数の関数のプロトタイプ宣言

int va_func(const char* types, ...); // (1)

仮引数は少なくてもひとつは必ず書く。次のように、仮引数がひとつもないとコンパイル
エラーになる。
int va_func2(...); // コンパイルエラー


(2) 関数の呼び出し

va_func("idcsxi", 12, 3.14, 'A', "Hello", 56, -78); // (2)

最初の引数が文字列であれば、あとは任意の型をいくつでも引数に取れる。
ただし、今回は、この va_func() の仕様として、文字列中で使用できる文字は、i, d, c,
s のいずれかで、これに対応した型として、引数の中で使用できるのは、int, double,
char, char* だけである。


(3) va_list 型の変数を定義

va_list list; // (3)

この変数は、これ以降の処理で使う。


(4) 可変長引数の処理の開始

va_start(list, types); // (4)

2番目の引数は、関数宣言の、可変長引数の直前の引数を使う。


(5) 仮引数の取得

printf(" int: %d\n", va_arg(list, int)); // (5)
printf("double: %f\n", va_arg(list, double)); // (5)
printf(" char: %c\n", (char) va_arg(list, int)); // (5)
printf("string: %s\n", va_arg(list, char*)); // (5)
va_arg(list, void*); // 空読みする // (5)

仮引数は、va_arg() を使う。

va_arg(v,l)
v: va_list 型の変数
l: 取得したい仮引数のデータ型

なお、つぎの型char は指定できず、警告が出る。実行すると、プログラムはアボートす
る。したがって、対応する型を指定して、キャストする。
char -> int
short -> int
float -> double

また、空読みするときは void* とする。void だと、読み捨てたい引数すら読まれない。


(6) 可変長引数の処理の終了

va_end(list); // (6)

'; type++) { switch (*type) { case 'i': printf(" int: %d\n", va_arg(list, int)); // (5) count++; break; case 'd': printf("double: %f\n", va_arg(list, double)); // (5) count++; break; case 'c': printf(" char: %c\n", (char) va_arg(list, int)); // (5) count++; break; case 's': printf("string: %s\n", va_arg(list, char*)); // (5) count++; break; default: printf("(bad type)\n"); va_arg(list, void*); // 空読みする // (5) break; } } va_end(list); // (6) return count; } --- □ 実行結果 --- int: 12 double: 3.140000 char: A string: Hello (bad type) int: -78 --- (1) 可変長引数の関数のプロトタイプ宣言 int va_func(const char* types, ...); // (1) 仮引数は少なくてもひとつは必ず書く。次のように、仮引数がひとつもないとコンパイル エラーになる。 int va_func2(...); // コンパイルエラー (2) 関数の呼び出し va_func("idcsxi", 12, 3.14, 'A', "Hello", 56, -78); // (2) 最初の引数が文字列であれば、あとは任意の型をいくつでも引数に取れる。 ただし、今回は、この va_func() の仕様として、文字列中で使用できる文字は、i, d, c, s のいずれかで、これに対応した型として、引数の中で使用できるのは、int, double, char, char* だけである。 (3) va_list 型の変数を定義 va_list list; // (3) この変数は、これ以降の処理で使う。 (4) 可変長引数の処理の開始 va_start(list, types); // (4) 2番目の引数は、関数宣言の、可変長引数の直前の引数を使う。 (5) 仮引数の取得 printf(" int: %d\n", va_arg(list, int)); // (5) printf("double: %f\n", va_arg(list, double)); // (5) printf(" char: %c\n", (char) va_arg(list, int)); // (5) printf("string: %s\n", va_arg(list, char*)); // (5) va_arg(list, void*); // 空読みする // (5) 仮引数は、va_arg() を使う。 va_arg(v,l) v: va_list 型の変数 l: 取得したい仮引数のデータ型 なお、つぎの型char は指定できず、警告が出る。実行すると、プログラムはアボートす る。したがって、対応する型を指定して、キャストする。 char -> int short -> int float -> double また、空読みするときは void* とする。void だと、読み捨てたい引数すら読まれない。 (6) 可変長引数の処理の終了 va_end(list); // (6)


コンストラクターで例外をキャッチする

2011年08月23日 | C / C++

基本クラスのコンストラクターでスローされた例外を派生クラスのコンストラクターの初
期化リストでもキャッチする
================================================================================

g++ではできるが、bcc32、VC++6では、コンパイルエラーになる。

つぎのように、派生クラスのコンストラクター部分を、初期化リストも含めて try-catch
で囲む。
この場合、初期化リストでも、コンストラクターの中身でも例外がキャッチされる。

□ ConstractorExceptionSample.cpp
---
#include <iostream>
#include <exception>
using namespace std;

class A {
public:
A() {
cout << "*A" << endl;
throw exception(); // 基本クラスで例外をスロー
}
};

class B : public A {
public:
B() try // 派生クラスのコンストラクターをtry-catchで囲む
: A() // 初期化リスト
{
cout << "*B" << endl; // コンストラクターの中身

} catch(exception &amp;e) {
throw e;
}

};

int main()
{
try {
cout << "*1" << endl;
B b;
cout << "*2" << endl;

} catch(exception &amp;e) {
cerr << e.what() << endl;
}
cout << "*3" << endl;

}
---

□ 実行結果(exception をスローした場合)
---
*1
*A
*3
St9exception <-- cerr なので、出る場所はこことは限らない
---


□ 実行結果(exception をスローしなかった場合)
---
*1
*A
*B
*2
*3
---

</pre>

JudeファイルからからC++のスケルトン・ソースを生成する

2008年10月09日 | C / C++
JudeApi2Cpp
JudeファイルからからC++のスケルトン・ソースを生成する

http://www.vector.co.jp/soft/other/java/se463798.html

動作OS: 汎用
動作機種: 汎用 
ソフトの種類: フリーソフト
概要:
JudeApi2Cpp は、Jude プロジェクトのファイル (.jude) から 、C++ のCPP ファイル(クラス名.cpp) と ヘッダー・ファイル (クラス名.h) を指定されたディレクトリーの中へ生成します。
あわせて、makefile も作成します。
また、標準のセッターとゲッターを生成させることもできます。

C 言語のソースコード・メトリクス測定ツール

2008年07月26日 | C / C++
C 言語のソースコード・メトリクス測定ツール

■ SourceMonitor Version 2.4
http://www.campwoodsw.com/sourcemonitor.html

C言語だけではなく、いろいろな言語(C++, C, C#, VB.NET, Java,
Delphi)のメトリクスを測定できます。
C 言語のメトリクスとしては、以下のものが取得できます。

・Lines: ソースファイル上の物理的な行数

・Statements: セミコロンで区切られた論理行数

・Percent Branch Statements: if, else, for, while, goto,
break, continue, switch, case, default, return の割合

・Percent Lines with Comments: C 形式 (/*...*/) または C++
形式 (//...) のコメント行数の割合

・Functions: 関数の数

・Average Statements per Function: 関数の中の論理行数の平均

・Maximum Function Complexity: 複雑度の最大値

・Maximum Block Depth: 関数の中のブロックのネスト数の最大値

・Average Block Depth: 関数の中のブロックのネスト数の平均値

・Average Complexity: 平均複雑度

以上

組込み現場の「C」プログラミング 標準コーディングガイドライン

2008年06月05日 | C / C++
組込み現場の「C」プログラミング 標準コーディングガイドライン
http://gihyo.jp/book/2007/978-4-7741-3254-9
福岡知的クラスタ(第1期)組込みソフト開発プロジェクト (著), 福田 晃 (編集, 監修)
単行本(ソフトカバー): 216ページ
出版社: 技術評論社 (2007/10/27)
言語 日本語
ISBN-10: 4774132543
ISBN-13: 978-4774132549
発売日: 2007/10/27
商品の寸法: 20.8 x 15.2 x 2.4 cm


第1部 バグを作り込まないためのガイドライン

ガイドライン01 つの変数には1つの役割だけを与える
変数を変身させると,正体を見失う

ガイドライン02 変数は最小のスコープになるようにする
目の届く範囲が手が届く範囲

ガイドライン03 変数の初期化は,わかりやすいところで行う
変数やレジスタの初期値は不定

ガイドライン04 文字列は,可能なら文字配列として扱う
char型ポインタはバグの温床

ガイドライン05 初期化に使う文字リテラルの長さはコンパイラで数える
数え間違えるのは人間.コンパイラは正確に数える

ガイドライン06 文字列では,最後をつねに意識する
文字列の終わりを示すヌル文字は単なる紳士協定

ガイドライン07 文字列の長さの表記は統一する
ヌル文字の数え方が破綻を引き起こす
C言語には文字列型はない

ガイドライン08 ブール型がないC言語では,真偽値の型と値を決めて使う
広く使用する定義はプロジェクトで統一する

ガイドライン09 モノの種類は列挙型で表現する
同じ種類は同じ枠組みで管理する

ガイドライン10 ポインタの使用をできるだけ制限する
ポインタのバグは大域的に影響する

ガイドライン11 複雑なポインタの使用はやめる
間接参照が重なると実体を見失う

ガイドライン12 ビットフィールドが有効なら活用する
ビットの操作はコンパイラに任せる

ガイドライン13 最後のセミコロン(;)をチェックする
終端忘れは後にも響く

ガイドライン14 複雑さは「()」で補う
暗黙の優先順位を明確にする
評価順序について

ガイドライン15 数値計算では,計算の誤差や演算エラーを考慮する
コンピュータは数学のようには計算しない

ガイドライン16 暗黙の型変換を避ける
密かに行われる型変換を意識させる

ガイドライン17 望みの型にするには,演算結果ではなくオペランドを
キャストする

演算結果をキャストしても,手遅れ

ガイドライン18 浮動小数点の比較は許容範囲を考慮する
浮動小数点数の計算には誤差がつきもの
実数を扱うときは誤差にも注意

ガイドライン19 関数型マクロの予期せぬ展開を避ける
「関数型」であっても,実際は,置換されて展開されるだけ

ガイドライン20 論理式はできるだけシンプルにする
複雑な条件は,判読/判定が困難になる

ガイドライン21 0との比較や代入は,数値の0との場合だけにする
0は必ずしも数値の「0」に非ず
副作用と副作用完了点

ガイドライン22 配列インデックスの名前と値に注意する
バグは境界やすぐ隣に潜む

ガイドライン23 ポインタが指している先を確認する
解放した領域は更新されているかもしれない

ガイドライン24 ポインタの型をつねに意識する
ポインタ型の取り違えでアドレス計算は狂う

ガイドライン25 構造体を扱う演算には,マクロか関数を用意する
構造体は「パッケージ」,バラバラに扱うと意味がない

ガイドライン26 トップダウンに読めるようにする
秩序のない視点の移動は思考の混乱を招く

ガイドライン27 関連するものはできるだけまとめる
散在させると注意も散漫になる

ガイドライン28 分岐やループの処理はブロック化する
制御の及ぶ範囲を明示する

ガイドライン29 分岐やループの制御部と処理は分離する
制御の仕組みを一目で認識可能

ガイドライン30 case節では,break文で終わらせるのを基本とする
独立であるべき事象に依存関係を持たせない

ガイドライン31 else節やdefaultラベル節を省略しない
熟慮した痕跡を記録しておく

ガイドライン32 適切なループ文を選ぶ
定石を逸脱したループは誤解のもと

ガイドライン33 ループ管理に関する処理は,先頭か末尾にまとめる
流れに従った記述をすれば,読むのもスムーズ

ガイドライン34 ループは先頭から入る
ループパターンの逸脱はプログラムを脆弱にする

ガイドライン35 ループからの脱出はわかりやすくする
抜け道は,迷い道

ガイドライン36 複数のファイルで共有する宣言は1箇所にまとめる
プロトタイプの不一致は暗黙の型変換を引き起こす

ガイドライン37 ヘッダファイルになにもかも詰め込まない
ヘッダファイルは実体を作るためのものではない

ガイドライン38 ヘッダファイルをしっかりと設計する
適切な情報隠蔽で分割コンパイルのメリットを享受する

ガイドライン39 ヘッダファイルの入れ子は慎重に行う
多重宣言のリスクを回避する

ガイドライン40 デバッグやテスト用のコードを「出荷」しないようにする
一歩間違えると,品質向上の手段で品質低下

ガイドライン41 assertを活用し,前提事項を確かめる
バグを積極的に炙り出す

ガイドライン42 コンパイラからのメッセージに耳を傾ける
コンパイラの苦言は良薬である
第2部 正しく人が理解するための ガイドライン

ガイドライン43 名前を大切にする
名は体を表すべし

ガイドライン44 違いがはっきりわかる名前にする
識別できるがゆえに「識別子」

ガイドライン45 名前の付け方に一貫性を持たせる
同じものを違う名前で呼ばない

ガイドライン46 変数には,実装方法ではなく対象の名前を付ける
知らしめるべきは,「それがなにか」

ガイドライン47 数字にも意味のある名前を付ける
マジックナンバーは情報を隠蔽する

ガイドライン48 真偽を表すものは,真偽が容易にわかる名前にする
なにをもって真とするかが重要

ガイドライン49 危険の徴候を名前で知らせる
大域的な注意の喚起は目立たせる

ガイドライン50 関数には役割を表す名前を付ける
「なにをどうする」/「なにを返す」を訴える

ガイドライン51 読み手に理解しやすい量を意識する
目で追えるから手に負える

ガイドライン52 1つの行は1つの文にする
タテ読み,ヨコ読みが混在すると,誤解/面倒のもと

ガイドライン53 1行は読みやすい長さにする
たいていの環境で収まる長さを心がける

ガイドライン54 引数の順序を統一する
インターフェースのアンマッチを防御する

ガイドライン55 caseの並べ方には規則性を持たせる
混沌の中に誤りは隠れる

ガイドライン56 レイアウトでプログラムの構造を可視化する
視覚的な整然さは,すなわち直感的なわかりやすさ

ガイドライン57 インデントは,確かなものに揃える
不定なインデントはレベルを誤解させる

ガイドライン58 コードを反復するようなコメントは避ける
自己解説的なコードは無駄なコメントを削減する

ガイドライン59 他人のためにコメントする
コメントは個人的な備忘録に非ず

ガイドライン60 変更の容易なコメントのスタイルにする
過剰装飾は資源(労力/メモリ)の無駄使い

ガイドライン61 コメントがコードの邪魔をしないようにする
補う情報で本体を隠さない

ガイドライン62 定石を逸脱するときはコメントする
読み手の先入観や違和感を払拭する

ガイドライン63 変数のコメントは,データ宣言の近くに簡潔に書く
遠い注釈は,見逃しと保守漏れの温床

ガイドライン64 関数のコメントは,関数を使う立場で書く
要旨は見出し.必要最小限に

ガイドライン65 ファイルのコメントには,モジュールの概要を書く
粒度に応じた要旨にする

ガイドライン66 「うますぎるプログラム」にしない
超絶技巧よりも単純明解を優先すべき

コーディングガイドライン チェックツールのポリシー
ツールの実行環境の構築方法
ツールの使い方
ツール一覧
チェックポイント一覧