C++でコマンドを作成する/Write a command in C++ ============================================== C++で書かれたソースコードをコンパイルして、いつでも使えるコマンド として、PATHで指定されたディレクトリに格納するための手順を紹介します。 Here we will show how to compile a C++ program and store it into a directory that is specified by the PATH environment variable, so that you can use it as a command at any time. ソースコード/The source code ---------------------------- 題材に使うのは、次のプログラムです。これは、ソースコードの文字コードを チェックするプログラムです。演習の中でソースを提出する前に、このプログラム でチェックしてください。長いのでタイプしてもらう必要はありません。 The following program is the subject. This is a character code checker program. Use this program to check your source when you submit source code during the seminar. You don't need to type in the code. .. literalinclude:: ../../programs/tutorials/source_checker.cpp :language: c++ :linenos: :download:`Download the source code<../../programs/tutorials/source_checker.cpp>` 作業用のディレクトリを用意する/Prepare a directory for the work --------------------------------------------------------------- プログラムをコンパイルするために、ホームディレクトリの下に ディレクトリを作ります。 Make a directory inside your home directory to compile the program. .. note:: ホームディレクトリに、過去の授業や演習などで作成したファイルが 残っている場合には、それらを格納するためのディレクトリをまず作って、 過去のファイルはそこにしまっておきましょう。 不要なファイルでしたら消してしまいましょう。 If your home directory is scattered with files from former classes, seminars, or any other activities, make a directory to stow those files away. If you think they are unnecessary, you can choose to delete them. ここではディレクトリ名を tools/source_checker とします。 Name the directory tools/source_checker:: $ cd $ mkdir -p tools/source_checker $ cd tools/source_checker 引数なしで :ref:`cd` を使うと、ホームディレクトリに移動します。 また、 :ref:`mkdir` に -p オプションをつけると、ディレクトリの階層を一度に 作ることができます。 Using cd with no arguments will take you to your home directory. By giving the -p option to the mkdir command, you can make multiple levels of directories at once. .. note:: 本文書に従って操作するときはは、(マウスでコピー&ペーストするのではなく)、 実際にタイプしてみて下さい。タイプすると覚えます。マウスでなぞっても、 マウスのなぞり方しか記憶に残らないようです。 タイプして覚えると、タイプを省力化するためのいろいろな仕掛けを活用できるようになります。 When you are following the instructions in this document, we urge you to actually type the commands, rather than copy-and-pasting them with a mouse. You will memorize by typing. Dragging the mouse only lets you memorize how to drag the mouse. Once you memorize through typing, you will be able to leverage the various tools to reduce the amount of typing. ソースコードをそこに配置する/Place the source code -------------------------------------------------- ソースコードのダウンロードリンクが、このページのソースコードのリストの 末尾にあります。作成したディレクトリにダウンロードしたソースを格納して 下さい。 もしもブラウザでデフォルトのダウンロードディレクトリに格納した場合には、 :ref:`mv` で source_checker ディレクトリに移動させてみてください。 ブラウザのダウンロード先ディレクトリはOSやブラウザによって異なるので、自分で 調べてください。 Get the source file from the download link at the end of the above source listing and store it in the directory you just created. If you stored the file to the browser's default download directory, try moving that file to the 'source_checker' directory with the mv command. The default download destination directory differs depending on browsers and OSes, so check for yourself:: $ pwd # make sure you are in the right directory /home/.../your-username/tools/source_checker $ mv ~/Downloads/source_checker.cpp . :ref:`pwd ` は、print working directory の略で、シェルのカレント ディレクトリを確認するために使えます。念の為、どこにいるのか確認したいときに 使います。 mv の後のチルダ "~" はシェルによってホームディレクトリのパス名に展開 されます。"~ユーザ名" とすると、任意のユーザのホームディレクトリを得る ことができます。 mv の最後の "." はカレントディレクトリのパス名です。ファイルの移動先として カレントディレクトリを指定しています。 :ref:`pwd` is short for "print working directory", and it does just that. You can use it to confirm where your shell is looking at. The tilde "~" after mv will be expanded to your home directory path name by the shell. The form "~username" can also be used and will be expanded to the home directory specified by the user name. The "." at the end of the mv command line is the path name for the current directory. The current directory is specified as the destination of the move operation. コンパイルする/Compile the code ------------------------------- C++のソースコードをコンパイルするにはC++用のコンパイラを使います。 コンパイラの種類がいろいろありますが、ここではLinux上で広く使える g++ を 使います。 To compile C++ source code, you need to use a C++ compiler. There are many compilers available, but here we will use g++, which is widely available on Linux:: $ pwd /home/.../your-user-name/tools/source_checker $ g++ -o source_checker source_checker.cpp 存在しているパス名をタイプするときは、TABキーによる入力補完が 効くことが多いので試してみてください。これから作り出すファイルに ついては無理です。 When you type an existing pathname, keep in mind that type assisting (input completion) is available through the tab key. However, this will not work for pathnames you are going to create. 実行する/Run the program ------------------------ プログラムを実行するには、プログラムのファイル名をシェルに入力します。 シェルはコマンド名を受け取ると、環境変数PATHに列挙されたディレクトリに コマンドを探しに行きます。"ls" コマンドなどはそのように実行されます。 一方、パスセパレータ "/" を含んだパス名をコマンド名として入力すると、 環境変数PATHとは関係なく、そのパス名を探しに行きます。 今、コンパイルして、出来上がったばかりの source_checker プログラムの ファイル名だけを入力すると、シェルはPATHの中を探しに行き、おそらく エラーになります。 手元のディレクトリにあるファイルを実行するには、プログラムのファイル名 だけを入力したのではだめで、パスセパレータ '/'を1つ以上含んだパス名を 指定する必要があります。"カレントディレクトリにある source_checker" でしたら、 ./source_checker がパス名になります。 To run a program, you enter the file name of the program to the shell. The shell will search for that file in the directories listed in the environment variable PATH. The "ls" command and others are executed in this way. On the other hand when you enter a path name of a file which includes at least one path separator "/", the shell will look directly at that pathname without using PATH. If you enter the file name "source_checker" into the shell, the shell will search that name in PATH, likely resulting in an error. To run a program file in the current directory, it is not enough to enter the file name. You must enter a path name with at least one path separator. "the source_checker file in the current directory" can be expressed by the path "./source_checker":: $ ls # make sure we are in the correct directory source_checker source_checker.cpp % ./source_checker source_checker.cpp Checking source_checker.cpp PATH経由で使えるようにする/make it usable via PATH -------------------------------------------------- 環境変数PATHで指定されたディレクトリにプログラムを置いておけば、 カレントディレクトリがどこであろうと、すぐにコマンドとして使う ことができます。 ここでは、ホームディレクトリの下に自作コマンド用のディレクトリを 設けて、環境変数PATHにそのディレクトリを指定してみます。 If you store your program in a directory that is included in the environment variable PATH, you will be able to invoke it as a command no matter where the current directory of your shell is. Here we will create a directory inside your home directory for storing your own commands, and point to that directory from PATH. まず、ホームディレクトリの下に .local/bin というディレクトリを 作ります。このディレクトリ名を使うツールも存在するので、人によっては 最初から出来ているかも知れません。 First, we make a directory .local/bin under your home directory. This directory name is used by some tools, so some of you may already have this directory:: $ ls # make sure we are in the correct directory source_checker source_checker.cpp $ mkdir -p ~/.local/bin $ cp source_checker ~/.local/bin 次に、環境変数PATHにこのディレクトリを登録します。 :doc:`環境変数の設定の仕方についてのチュートリアル` も合わせて読んでください。 Next, we add an entry for this new directory in PATH. Also read the tutorial on environment variables. $ PATH=$PATH:~/.local/bin $ export PATH これで準備ができました。いまや先頭の "./" をつけずにコマンドとして起動できます。 We are done. You no longer need to add the "./" in front of the file name:: $ source_checker *.cpp Checking source_checker.cpp プログラムの中身/The content of the program ------------------------------------------- プログラムの中身について簡単に説明します。 このプログラムは文字コードのチェックツールであり、 シミュレーション計算と直接の関係はありませんが、 シミュレーションプログラムの実装にも役立つテクニックをいくつか使っています。 このチュートリアルに取り組んだ時点で難しいと感じる場合は、少しC++言語に 慣れてから読んでみてください。 We will briefly explain the content of the program. This program is a character code checker tool, and is not related with simulation calculation. However it uses some techniques that can be applied to simulation programs. If you find the content difficult at the time you work on this tutorial, you can revisit the content later when you get more used to the C++ language. main関数/The main function ^^^^^^^^^^^^^^^^^^^^^^^^^^ .. literalinclude:: ../../programs/tutorials/source_checker.cpp :language: c++ :lines: 8-21 :linenos: :lineno-start: 8 main関数は、argvで渡されたパス名をひとつずつ checkOneFile 関数に渡します。 checkOneFileは、パス名の誤りなどによって処理に失敗した場合にはfalseを返します。 処理に失敗した時点でループは終了します。 main関数の戻り値は、プロセスの終了ステータス値として、呼び出し元の親プロセス (典型的にはシェル)に伝達されます。 Linuxも従う :term:`POSIX規格` では終了ステータスの値として 0または ``EXIT_SUCCESS`` は正常終了を意味し、 ``EXIT_ERROR`` は、異常終了を意味します。 The main function will loop through the path names given as argv, and calls the checkOneFile function for each path name. checkOneFile will return false on a failure. It can fail if the given pathname is not readable. Upon failure, the loop will be terminated. The return value of the main function will become the exit status code of the process and will be sent to the parent process, which typically is a shell. Acoording to the :term:`POSIX Standard`, which Linux complies, the exit status value 0, or ``EXIT_SUCCESS`` denotes a success, and ``EXIT_ERROR`` denotes an error. The checkOneFile function ^^^^^^^^^^^^^^^^^^^^^^^^^ .. literalinclude:: ../../programs/tutorials/source_checker.cpp :language: c++ :lines: 65-101 :linenos: :lineno-start: 64 checkOneFile関数では、ファイルから1バイトずつ読み込むために、std::ifstream クラスの変数 ifs を作ります。コンストラクタ引数にはファイル名と、 :term:`バイナリモード` を指定するフラグを渡しています。 バイナリモードを指定しないと、ifstreamは空白、タブ、改行などのいわゆる whitespace character を全て区切り文字として読み飛ばしてしまいます。 このプログラムではタブ文字を検出したり、改行を数えたりしたいので、 バイナリモードを指定します。 ifstreamに指定したパス名に問題があると、ファイルのオープンに失敗します。 成功したか失敗したかを、is_open() メソッドで確認しています。 perror関数は、C/C++標準ライブラリの中で生じたエラーの種類を説明するメッセージを 印字する関数です。perror関数の引数には、ファイル名など、そのときの操作の 対象だったモノの名前を渡します。この名前は、メッセージの一部に使われます。 メッセージとしては、以下のようなものが得られます:: no such file or directory: spel_misteik.cpp ファイルのオープンに成功したら、while文の中で1バイトずつファイルを読んでいきます。 whileの条件には変数 ifs をそのまま渡しています。 while ( 式 ) の式の部分には論理値 (bool型の値) が求められます。 ifsはbool型ではなく std::ifstream クラスです。std::ifstreamクラスには、bool 型への変換関数が設けられており、その時点でストリームからデータをさらに 読み出すことができれば true, 読み出すことができなければ false を返すように なっています。そのため、この書き方で、「ファイルからデータが読み取れる間は 繰り返す」と書いたことになります。 関数の残りの部分では、ファイルに登場する文字コードのチェックをしています。 以下の事項をチェックしています。 * タブ文字は登場しないか。 * 改行文字として、 :term:`Unix形式の改行` 以外のものが使われていないか。 * 改行以外の制御コード(Backspace, Beep, Form feed等)が登場していないか。 * マルチバイト文字は :term:`UTF-8` の規則に則っているか。Windowsの漢字コードを 使うと、このチェックに抵触します。 * ファイルの最後の改行の後に、文字がないか。つまり、最終行の末尾の改行を 忘れていないか。 .. literalinclude:: ../../programs/tutorials/source_checker.cpp :language: c++ :lines: 98-215 :linenos: :lineno-start: 97