Home > C言語 Archive

C言語 Archive

前方一致

検索用の文字列比較関数をリファクタリングしてたんです。
最初こんなんでした。


int compare(char *a, char *b) {
   int length_a;
   int length_b;
   int min_length;
   int i;
   int result = 0;
   
   length_a = strlen(a);
   length_b = strlen(b);
   min_length = min(length_a, length_b);
   
   for (i = 0; i < min_length; i++) {
      if (a[i] > b[i]) {
         result = 1;
         break;
      } else if (a[i] < b[i]) {
         result = -1;
         break;
      }
   }
   
   if (result == 0) {
      if (length_a > length_b) {
         result = 1;
      }
   }
   return result;
}

なんか変なことやってるなーと思い、「要は文字列比較だろ?」っていうわけで以下のように『リファクタリング』しました。


int compare(char *a, char *b) {
   int result = 0;
   
   for (i = 0; ; i++) {
      if (a[i] == '\0') {
         if (b[i] == '\0') {
            break;
         } else {
            result = -1;
	        break;
	     }
      } else if ( b[i] == '\0') {
         result = 1;
         break;
      } else if (a[i] > b[i]) {
         result = 1;
         break;
      } else if (a[i] < b[i]) {
         result = -1;
         break;
      }
   }
   
   return result;
}

うむ、合っているはずだ!というか、こんな比較関数を作るくらいならstrcmp使えって話なんだけど、そのときは気づかず。勝利を確信し動かしてみたらちゃんと動かねえ。CppUnitがFalseを出しまくりやがります。テストコードを見ると、なんとこの関数のテストコードがない。「いっけね、忘れてた」ってわけで早速テストコードを作成しテストしてみると、どうやらこの関数は正しく動いとる。むしろ元のコードは新しく作ったテストコードを通過しない。「このプログラムはバグの上に成り立っているのかぁ!?」と、ふとコメントを見ると
『前方一致に対応するためaの長さまでで比較する』
と書いてある。なにぃ!そうだったのか!つまり、a=”abc”, b=”abcde”の場合、0を返さないといけない。・・・ああ、だから最初あんな変なコードになっていたのか。というわけで最終的に下記のようになりました。


int compare(char *a, char *b) {
   int result = 0;
   
   for (i = 0; ; i++) {
      if (a[i] == '\0') {
         break;
      } else if ( b[i] == '\0') {
         result = 1;
         break;
      } else if (a[i] > b[i]) {
         result = 1;
         break;
      } else if (a[i] < b[i]) {
         result = -1;
         break;
      }
   }
   
   return result;
}

ちゃんと動いた。すごく微妙に早くもなった。CppUnitが役に立ったな。

以上

CppUnitの使い方 4~登録マクロ生成(おまけ)

CppUnitの使い方のおまけ。
テストコードを書いたはいいものの、テストスイートに登録するためのマクロを書くのが結構めんどくさい。
そういうわけで
test_関数名_連番()
みたいな名前にしてるときにマクロを生成してくれるちょんスクリプトをメモ。
ソースコードをコマンドライン引数で渡す。


<?php

if (empty($argv[1])) exit(1);

$file = file($argv[1]);

$result = array();
foreach ($file as $line) {
    if (preg_match('/.*void\s+(test.+?)\(.*/i', $line, $matches)) {
        $result[] = "CPPUNIT_TEST(" . $matches[1] . ");";
    }
}

foreach ($result as $line) {
    echo $line . "\n";
}

?> 

phpだけど。

以上

CppUnitの使い方 3~ソースの書き方

CppUnitの使い方ラスト。

目次
1.準備
2.プロジェクトの構成
3.ソースの書き方

まずはメイン。コンソールでテキストベースに使う場合はHogeTest.cppを次のように書く。HogeTest.cppやらファイルの構成やらについては前回を参照のこと。


#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/TextOutputter.h>
#include <cppunit/extensions/TestFactoryRegistry.h>

int _tmain(int argc, _TCHAR* argv[])
{
    CppUnit::TextUi::TestRunner runner;
    runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
    CppUnit::Outputter* outputter = new CppUnit::TextOutputter(&runner.result(),std::cout);
    runner.setOutputter(outputter);
    int ret = runner.run() ? 0 : 1;
    return ret;
}

Hogeクラスのテストコードは以下のようにする。


// 必要なヘッダの #include
#include "Hoge.h"
#include <stdio.h>
#include <stdlib.h>
#include <string>

#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestAssert.h>


class Hoge_Test : public CppUnit::TestFixture {
    // テストスイート作成
    CPPUNIT_TEST_SUITE(Hoge_Test);  // クラス名を指定してテストスイート(テストの集合)を作成する。
    CPPUNIT_TEST(test_foo_001);  // 下で定義したテストコードを登録する。これを書かないとそのテストはテストされない。
    CPPUNIT_TEST_SUITE_END();
private:
    // 必要な メンバ変数/関数

    // テスト用ツール関数はこの辺に定義しとく。以下はサンプル。
    
    /**
     * 整数を文字列に変換する。
     */
    std::string itos(int i) {
        char buff[50];
        sprintf(buff, "%d", i);
        return std::string(buff);
    }

    /**
     * 乱数を発生させる。0以上N未満。
     */
    int my_rand(int N) {
        return (int)((double)rand() / ((double)RAND_MAX + 1) * N);
    }





public:

    /*
     * コンストラクタ/デストラクタ
     */
    Hoge_Test() {
    }

    ~Hoge_Test() {
    }

    /*
     * 初期化。
     * 各テストコードを実行する前に呼ばれる。
     * 内部変数を初期化したいとか、そういうのを書く。
     */
    virtual void setUp() {
    }

    /*
     * 後始末。
     * 各テストコード終了後に実行される。
     * たぶん、テストに失敗しても呼ばれるんじゃないかな。
     */
    virtual void tearDown() {
    }


    /**
     * 実際のテストコードを書く。
     * 名前はたぶんなんでもいいが、分かりやすさのため
     * test_関数名_番号()
     * としておく。引数・戻り値はvoid。
     */
    void test_foo_001() {
        Hoge instance;
        
        CPPUNIT_ASSERT(true == instance.foo());  // foo()がtrueを返すかどうかをテスト。
        CPPUNIT_ASSERT("foo test", true == instance.foo());  // 上と同じ。エラーのとき「foo test」って出る。
        CPPUNIT_ASSERT_EQUAL(true, instance.foo());  // instance.foo()がtrueと同じかのテスト。これ使わなくても上でいいんじゃね?
        CPPUNIT_ASSERT_DOUBLES_EQUAL(0.1, instance.foo(), 0.001);  // instance.foo()が0.1を返すかどうか。浮動小数点なので。誤差0.001。
        CPPUNIT_FAIL("error");  // 失敗する。エラーパスに仕込むのかな。あるいはまだ中身作ってないテストとか?
    }

};

/*
 * テストスイート(このクラスの冒頭で登録した奴)をテストとして登録。
 * これをしないとこのスイートはテストされない。
 * 逆に言うと、これさえ書いておく、あるいは消しておけばメインを変更しなくてもテストするクラスを指定できる。
 */
CPPUNIT_TEST_SUITE_REGISTRATION(Hoge_Test);

そういうわけで、privateなメソッドをテストするためにHogeクラスは以下のようにしておく。(参考:前方宣言


class HogeTester;   // 前方宣言

class Hoge {
   friend HogeTester;

private:
   bool foo;
   void bar();
};

C言語のソースでも同じように適当にテストクラス作ってテストコード作ってやればいい。ファイルスコープ関数(staticなやつ)をテストするには*.cファイルをインクルードするとかいう荒業もありか。(なしかも)

以上

CppUnitの使い方 2~プロジェクトの構成

そういうわけでCppUnitの使い方、個人的な使い方の続きを。

目次
1.準備
2.プロジェクトの構成
3.ソースの書き方

みたいな感じで今回は「2.プロジェクトの構成」

VCだとソリューションがあって、その下にいくつかプロジェクトをぶら下げられるので、メインプロジェクトとテストプロジェクトを同じソリューション下におく。また、前回で作成したlibやcppunitのヘッダをインクルードするため、それらをテストプロジェクト下におく。
例えばディレクトリ構成は下記のようにする。

プロジェクト名:Hoge(=ソリューション名)
Hoge/
  -source/                # この下はメインプロジェクトのソース
      -Fuga.cpp
  -header/                # この下はメインプロジェクトのヘッダ
      -Fuga.hpp
  -HogeTester/
      -HogeTest.cpp             # テストプロジェクトのmainソース
      -Fuga_Tester.cpp         # Fuga.cppのテスト用ソース
      -cppunit-1.12.0/          # cppunitのいるとこ抜粋してコピーする
          -include/                   # cppunit/ とか msvc6/ とかを丸々置いておく
          -lib/                  # 作成したlibファイル群(cppunit.lib, cppunitd.libなど)

HogeTesterプロジェクトのプロパティでリリースモード、デバッグモードそれぞれでパスを設定する。

・追加のインクルードディレクトリ (C/C++)
  ../header               # メインプロジェクトのヘッダ
  cppunit-1.12.0/include         # cppunitのヘッダ
・追加のライブラリディレクトリ (リンカ)
  cppunit-1.12.0/lib
・追加の依存ファイル (リンカ)
[リリースモードの場合]
cppunit.lib TestRunner.lib
[デバッグモードの場合]
cppunitd.lib TestRunnerd.lib

C/C++のコード生成でランタイムライブラリは前回の通り

[リリースモードの場合]
マルチスレッドDLL
[デバッグモードの場合]
マルチスレッドデバッグDLL

としておく。
あと経験上、プリコンパイル済みヘッダーは使用しないほうがトラブルに巻き込まれずにすむ。プロジェクトが固まってきたら使用してもいいかも知れんけど。でっかいプロジェクトだとコンパイル遅いしな。

次回は実際のコードのテンプレートを示す。

以上

CppUnitの使い方 1~準備編

そういうわけで、CppUnitの私なりの使い方を3回くらいに分けてメモっておく。
VCでコマンドプロンプトでテキストベースで使うという、限定的な使い方だけども!

目次
1.準備
2.プロジェクトの構成
3.ソースの書き方

見たいな感じで今回は「1.準備」

CppUnitは要するにC++のユニットテストをするためのフレームワーク。
いろんな組み込み方はあるだろうけども、私はlibファイルを作って静的リンクにより利用するのが簡単で好きだ。
そういうわけで、まずはここからダウンロード
解凍したらsrcフォルダのVCプロジェクトを開く。
そんでもってビルド!リリースビルドとデバッグビルドをやっておく。
ここでなんかしらんけども、プロジェクトのランタイムライブラリはマルチスレッドDLL(リリースの場合)、マルチスレッドデバッグDLL(デバッグの場合)じゃないとうまくできなかった。そういうわけで、テスト対象もこのどっちかになってしまう。うーん、どうにかなるのかね?
ビルドしたらlibファイルができる。dllとかGUIっぽいexeもできるがそんな軟弱なものはとりあえず使わない。そんなのはすげぇなれてから、ひまでしかたなかったらやってみればいい(と思って3年くらい使ってない)。

というわけで、実際のプロジェクトからlibファイルをリンクして使うわけだが、そのときのディレクトリ構成とかは次回メモる。

以上

Doxygenでヘルプファイルを作りたいんだけど

CPPUNITは最初の導入がめんどいな。今度書く。

で、Doxygenなんですけど1.5.4あたりからヘルプファイル(.chm)を作ったときに日本語が文字化けする。
1.5.5が出てたんですけど、やっぱり文字化けする。
というわけでとりあえずの対処法。

1.普通にヘルプファイルを作ろうとする。文字コードはインプットとアウトプットと両方指定しておいたほうがいいかも。指定できる文字コードはたぶんこれ。アウトプットはUTF-8がいい気がする。
2.できたヘルプファイルを開いて左側のメニューが文字化けしていることを確認する。あと経験ではプロジェクト名に日本語を使っていると本文のプロジェクト名の部分も?????になる。(これはもう英語にするしか?)
3.index.hhcファイルをテキストエディタで開き、SHIFT_JISで保存する。メモ帳ではだめかもわからん。とにかくUTF-8⇒SHIFT_JISへの変換を行う。
4.コマンドプロンプトで”C:\適当なパス\hhc.exe index.hhp”を実行する。

以上

(2010/04/21 追記)
いつの間にか「CHM_INDEX_ENCODING」という設定項目ができていた。
OUTPUTもCHM_INDEX_ENCODINGも文字コードに「CP932」を指定しておけば、めんどくさいことをしなくても文字化けしないchmが作れる。

以上

宣言宣言

宣誓!私は!C言語を書くときはプロトタイプ宣言をすることをここに誓います!

というわけで、ちょっとしたぼやきですが、プロトタイプ宣言を書いてくれてる方が私は好きです。
C言語における関数の場合、主にグローバルスコープとファイルスコープの関数があります。(細かく言えばブロックスコープもあるかもしれない。)
いろいろ人のソースを見てるとその辺が適当になってたりする。1ファイルからしか参照されていない(またそうあるべき)関数をグローバルスコープにしたり、ヘッダを作らずソースファイルごとにプロトタイプ宣言をしていたり、プロトタイプ宣言をせずに関数定義だけで、定義以降に書かれている関数からだけ参照したり。
一般にはヘッダ(*.h)に関数宣言を書いてソース(*.c)に関数定義を書くと思います。どうしようもなく初歩的なことですが、今後考えが変わる可能性もあるので現在の私のC言語の書き方をまとめておきます。

1.大まかな機能ごとにソースファイルを分ける。
2.ソースと同名のヘッダファイルを作り関数宣言をする。複数のソースの関数をまとめたようなヘッダは作らない。
3.1つのソース内だけで参照される関数はファイルスコープにする。つまりstaticをつける。
4.共通の定数定義やマクロ関数を作る場合はそれ用のヘッダを作る。
5.関数にはJavadoc形式でコメントをつける。コメントは関数宣言、関数定義のどちらに書いてもよいが、宣言のほうを正とする。
6.引数をとらない関数はちゃんとvoidをつける。C言語では宣言でhoge()だと関数定義で引数をとってもよいことになってる。C++だとvoidと同義。
7.(今後思いついたら追記)

ヘッダ(template.h)のテンプレート:


#ifndef TEMPLATE_H__       // 頭にアンダースコア(_)はつけない
#define TEMPLATE_H__
// ////// ヘッダインクルード ///// //
#include <standard.h>
#include "original.h"

// ///// 定数定義 ///// //
#define VALUE (100)

// ///// マクロ関数 ///// //
#define FUNC(X) ((X)*(X))

// ///// 関数宣言 ///// //
/**
 * 説明文。Javadoc形式。
 * @param time 時間
 * @return 0:正常、1:エラー
 */
int sample(int time);

#endif   // TEMPLATE_H__

ソース(template.c)のテンプレート:


// ///// ヘッダインクルード ///// //
#include "template.h"
#include <some.h>
#include "myhead.h"

// ///// 内部関数宣言 ///// //
/**
 * ファイルスコープの関数。
 */
static void test(void);

// ///// 関数定義 ///// //
/*
 * 説明文。Javadoc形式。
 * @param time 時間
 * @return 0:正常、1:エラー
 */
int sample(int time) {
   return 1;
}

/*
 * ファイルスコープの関数。
 */
void test(void) {
   return;
}

以上

C言語 ビット演算?

原油高のせいか、油が足りません。
スーパーでもらったレジ袋を開けられないこと約1分。指をなめると開けられるんでしょうが、負けた気がするのでなんとか爪で突破口を開きました。

C言語だとよくビット演算やらビットシフトやらをやると思います。


unsigned long a = 0x80;
unsigned long b;

b = a>>1;

しかしこの右シフト、符号付整数と符号なし整数では動作が違う。ということを昨日知った。
符号付の場合は算術シフト、符号なしの場合は論理シフト。
算術シフトでは符号をあらわすビット、つまり先頭のビットは移動しない。
だから例えば


long a = -2;
long b;

b = a>>1;

とするとbは-1になるという。


long a = -2;
long b;

b = ((unsigned int)a)>>1;

だとbは2147483647になるという。

というわけなんですが、実は符号付整数の右シフトが算術シフトになるか論理シフトになるかは処理系依存らしいです。一般的には算術ですが。
あと右シフトだけでなく符号付整数のビット演算は全部処理系依存って話も。未確認ですが。

以上

C言語のライブラリ

C++じゃなくてC言語でHashtableみたいなのないの?ん?と思ってたら、ありますね。
APRだってさ。Apacheだしいいかもしれない。ほかにもいろいろ便利そうなライブラリですな。

以上

C++トリビアルメモ1

すごい簡単に作れるので作ってみた。
http://kosumi.myminicity.com/
コスミー町です。訪れてくれると育つらしい。

ひさびさにC++をさわるといろいろバグる・ハマる。というのは以前にも言った気がする。

1.クラスの前方宣言とインスタンス
例えばfriendを使う場合、friendしたいクラスを認識するにはfriendする前にそのクラスが宣言されていないといけない。つまり、


class HogeTester;   // 前方宣言

class Hoge {
   friend HogeTester;

private:
   int foo;
   void bar();
};

みたいな感じで型だけを先に宣言する「前方宣言」が必要となる。これを使わないとHogeTesterの中でHogeを使いたいはずだから入れ子になってしまう。
で、これはいい。これはいいんだけど、これに習って例えば内部クラスの定義を下のほうに書きたいなーとか思うとハマる。


class Hoge {
public:
   class Foo;
   Foo bar;

   class Foo {
   public:
      int a;
   };
};

これは無理。barがインスタンスなため、Fooのデータサイズが分からないとコンパイルのしようがないんだろう。よく考えればそりゃそうかもしれないが、Javaとかやってるとこれくらい許してほしいと思ってしまう。
コンパイル可能なのは


class Hoge {
public:
   class Foo {
   public:
      int a;
   };

   Foo bar;
};

あと、以下のような場合はOKっぽい。


class Hoge {
public:
   class Foo;
   Foo *bar;
   Hoge() { bar = new Foo(); }

   class Foo {
   public:
      int a;
   }
};

ポインタのため。

あと2個ほどはまったんだけど、意外と長くなったのでエントリ分けることにする。
ああそうだ、しょーもない話では


double a = 2/3;

としたらaが0になってはまった。スクリプト言語ばっかやってるとこうなる(スクリプト言語でも気をつけろという話だろうが)
正しくは


double a = 2.0/3.0;

小数点をつけないとintに丸められてしまうとさ。

以上

Home > C言語 Archive

Return to page top