mcommit's message

大阪でソフトウェア開発の仕事をしている simotinといいます。記事の内容でご質問やご意見がありましたらお気軽にコメントしてください\^o^/

Pocoを使ってHTTPS通信するコードを書いてみる

少し前に知ったのですが、C++にPocoというとても便利なライブラリがあります。
HTTPS通信を実装する際に使ったのですが、便利だったので使い方等を書いておきたいと思います。

幅広い機能

PocoにはJSONXMLのファイルフォーマット処理の機能があります。
また、ほかのライブラリに依存はしますが、SQLite,MySQL,ODBCのようなデータベースに関する機能、
FTPやHTTPといったネットワークに関わる機能など幅広い機能も提供されています。

https://pocoproject.org/features.html

ライセンスは?

Boost Software Licenseで公開されています。
基本的にはライブラリを利用するにあたって、ソースコード著作権の表示があれば(公開を含めて)無償で利用できるそうです。

https://pocoproject.org/license.html

使い方

ここからソース一式をダウンロードします。
https://pocoproject.org/download/index.html

私が利用したのはUnix/Linux系向けの方ですが、Windows用のソースも提供されているようです。(VisualStdioでビルドできるようですね)
タイトルにも書きました、「HTTPS通信」には「Complete Edition」の方をダウンロードする必要があります。
HTTPS通信の機能はOpenSSLに依存しています。ここでは、OpenSSLはインストール済みのものとして説明します。

ビルドについて

Pocoの実体は各機能を提供する共有ライブラリ(.so)として生成されます。
./configure時に --static をつけることで静的ライブラリとしてビルドすることも可能です。
※試していませんがCMakeにも対応しているみたいです。

オプションがたくさんありますが、


$ ./configure --help

でコンフィグレーションに関するオプション情報を確認できます。

ビルド対象とするコンポーネントについても--omit オプションで取捨選択できます。
どのようなコンポーネントがあるかは、componentsというファイルに一覧が記載されています。


$cat components
CppUnit
CppUnit/WinTestRunner
Foundation
XML
JSON
Util
Net
Crypto
NetSSL_OpenSSL
Data
Data/SQLite
Data/ODBC
Data/MySQL
MongoDB
Zip
PageCompiler
PageCompiler/File2Page

ビルドまでのコマンド実行例


$ wget https://pocoproject.org/releases/poco-1.7.8/poco-1.7.8p3-all.tar.gz
$ tar xzfv poco-1.7.8p3-all.tar.gz
$ cd poco-1.7.8p3-all
$ ./configure --no-tests --no-wstring --omit=Data/SQLite,Data/MySQL,Data/ODBC,Zip,MongoDB,PageCompiler,PageCompiler/File2Page
$ make
$ sudo make install
$ sudo ldconfig

./configure は一瞬で終わるのですが、ビルドにはそれなりに時間がかかると思うので不要な手順は省略するようにオプションを指定した方がよさそうです。


デフォルトでは、ビルドされたライブラリ(.so)は /usr/local/lib にインストールされます。
ヘッダファイルは、/usr/local/include/Poco 以下にインストールされます。

ライブラリを使う前に ldconfig でライブラリ情報は更新しておく必要があります。

HTTPS通信のコードを書いてみる。

早速HTTPS通信をするコードを書いてみます。

#include <string>
#include <iostream>
#include <sstream>

#include <Poco/URI.h>
#include <Poco/Net/HTTPSClientSession.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/StreamCopier.h>
#include "Poco/Net/Context.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/AcceptCertificateHandler.h"
using namespace std;

int main(int argc, char **argv)
{

	// SSL接続情報 初期化
	Poco::Net::initializeSSL();
      Poco::Net::SSLManager::InvalidCertificateHandlerPtr ptrHandler ( new Poco::Net::AcceptCertificateHandler(false) );
      Poco::Net::Context::Ptr ptrContext ( new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "") );
      Poco::Net::SSLManager::instance().initializeClient(0, ptrHandler, ptrContext);


	try {
		Poco::URI uri("https://www.yahoo.co.jp/");
		Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort());
		Poco::Net::HTTPRequest req("GET", uri.getPath(), Poco::Net::HTTPMessage::HTTP_1_1);

		// リクエスト送信
		ostream& ss = session.sendRequest(req);

		// レスポンス受信
		Poco::Net::HTTPResponse res;
		istream& rs = session.receiveResponse(res);

		stringstream sstr;
		Poco::StreamCopier::copyStream(rs, sstr);
		string response = sstr.str();
		cout << string("response:") + response << endl;
	}
	catch ( Poco::Exception& ex )
	{
		string msg = string("Poco Exception : ") + ex.what() + ", message: " + ex.message();
		cout << msg << endl;
	}

	return 0;
}

コンパイルと実行

HTTPS通信でリンクする必要があるライブラリはPocoNetSSLになります。
※StreamCopierとか使っているのでたぶんPocoUtilとかもリンクする必要があるように思いますが、PocoNetSSLでリンク指定しているのか、手元の環境では、ビルド時に指定しなくても動きました。



$ g++ main.cpp -lPocoNetSSL #上記のコードをmain.cppで保存した場合
$ ./a.out # 正常に実行されると、yahooのトップページが取得・表示されます

pocoのクラスについて

サーバーとのHTTPS通信は HTTPSClientSession クラスを使って行います。
通信を始める前にSSLManagerの接続情報を初期化しておく必要があります。
OpenSSL をインストールしていれば上記のようなコードで、OpenSSL のデフォルトの証明書を使って通信されます。

認証に使用する証明書を指定したい場合や、パスフレーズを使う場合は、Context クラスに証明書のファイルパスやパスフレーズ等を細かく指定する必要があります。
パスフレーズを使う場合は、PrivateKeyPassphraseHandler クラスを継承して、ライブラリ側にパスフレーズを受け渡しするコードを書く必要があります。

Poco のクラスのリファレンスは、

https://pocoproject.org/docs/

にあります。

HTTP通信してみるコード

ちなみに、SSLを使わない通常のHTTP通信をする場合は、 HTTPSession クラスを使うことで可能です。

#include <string>
#include <iostream>
#include <sstream>

#include <Poco/URI.h>
#include <Poco/Net/HTTPClientSession.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/StreamCopier.h>
using namespace std;

int main(int argc, char **argv)
{
	try {
		Poco::URI uri("http://www.hatena.ne.jp/");
		Poco::Net::HTTPClientSession session(uri.getHost(), uri.getPort());
		Poco::Net::HTTPRequest req("GET", uri.getPath(), Poco::Net::HTTPMessage::HTTP_1_1);

		// リクエスト送信
		ostream& ss = session.sendRequest(req);

		// レスポンス受信
		Poco::Net::HTTPResponse res;
		istream& rs = session.receiveResponse(res);

		stringstream sstr;
		Poco::StreamCopier::copyStream(rs, sstr);
		string response = sstr.str();
		cout << string("response:") + response << endl;
	}
	catch ( Poco::Exception& ex )
	{
		string msg = string("Poco Exception : ") + ex.what() + ", message: " + ex.message();
		cout << msg << endl;
	}

	return 0;
}


依存するクラスが減るのでコードも少しシンプルになりますね。
HTTP通信の場合はビルドにリンクするライブラリはPocoNetの方になります。

参考

Pocoの各コンポーネントにはサンプルプログラムが付属しています。
HTTPS通信の場合は、NetSSL_OpenSSL/samplesに

  • HTTPSでのダウンロード(HTTPSクライアント)
  • 時刻サーバ
  • Twitterクライアント

といったサンプルコードがあります。

感想

C++のライブラリというとBoostくらいしか知りませんでしたが、Pocoは非常に便利・高機能だなという印象を受けました。
あと、リファレンスもきちんと提供されていて、クラスの階層が奇麗なのでわからないことがあった時もソースを追っかけやすかったです。

Linuxでシリアル通信のプログラム(C言語)を書く

Linux上でC言語でシリアル通信をするプログラムを書く際に、いろいろ調べたので書いておきたいと思います。

シリアル通信プログラムの流れ

Linuxでシリアル通信プログラムを書く際の大まかな流れですが、

  1. シリアルポートに対応するデバイスファイルをオープンする
  2. termios構造体を使って通信設定する
  3. termios構造体の設定値をポートに反映させる
  4. read/write関数を使って通信する

というような流れになります。

シリアルポートのデバイスファイル

一般的にUnix/Linux系OSではシリアルポートは/dev/ttyS* にマッピングされています。tty はテレタイプ(teletype)を意味するそうです。

termios構造体

termios構造体を使ってシリアル通信のための設定をします。

■参考にするmanページ

https://linuxjm.osdn.jp/html/LDP_man-pages/man3/termios.3.html

設定処理は結構面倒臭い感じになりますが、おおむねビット毎にパリティやらデータビットやらの役割が決められているような感じですね。

制御コードに気をつけろ!

シリアル通信のプログラミングで気を付けないといけないことがいくつかあります。
termios構造体は名前の通り端末との通信を扱うことを意識して設定されています。

そもそもUNIXは現代のパソコンとは使い勝手の異なるコンピュータでした。
Unixシステムには、端末と呼ばれるキャラクタ画面とキーボードがついた装置が複数つながれていて、何か計算をしたい人たちはこの「端末」からログインし、コンピュータを使っていました。
そして、termios構造体はこの「端末」との入出力を制御するために使われていたのです。
今となっては懐かしい時代ですね・・・産まれてなかったけど。

前置きが長くなりましたが、センサや各種デバイスと通信する際は生データ(8bit)で通信することが多いと思いますが、
termios構造体を使う場合そのままでは生データとして受信できません上位ビットがマスクされてしまうようです。

生データを扱う場合は、cfmakeraw関数により設定値を生データの送受信用の値にしてあげる必要があるそうです。

ちなみに、cfmakerawの中の人は、

 termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    termios_p->c_oflag &= ~OPOST;
    termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    termios_p->c_cflag &= ~(CSIZE | PARENB);
    termios_p->c_cflag |= CS8;

をしてくれているそうです。

raspberry piで動かしてみる。

というわけでtermios構造体を使うC言語のコードを書いてみました。
以前Raspberrypiで、Rubyを使ってシリアル通信をする記事を書きましたが、

mcommit.hatenadiary.com


今回はtermios構造体を使うC言語のコードを書いて、Raspberry piで動かしてみました。
ソースコードはこんな感じです。

Raspberry pi で動かしてみたコード(C言語)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>

#define SERIAL_PORT "/dev/serial0"

int main(int argc, char *argv[])
{
    unsigned char msg[] = "serial port open...\n";
    unsigned char buf[255];             // バッファ
    int fd;                             // ファイルディスクリプタ
    struct termios tio;                 // シリアル通信設定
    int baudRate = B9600;
    int i;
    int len;
    int ret;
    int size;

    fd = open(SERIAL_PORT, O_RDWR);     // デバイスをオープンする
    if (fd < 0) {
        printf("open error\n");
        return -1;
    }

    tio.c_cflag += CREAD;               // 受信有効
    tio.c_cflag += CLOCAL;              // ローカルライン(モデム制御なし)
    tio.c_cflag += CS8;                 // データビット:8bit
    tio.c_cflag += 0;                   // ストップビット:1bit
    tio.c_cflag += 0;                   // パリティ:1bit

    cfsetispeed( &tio, baudRate );
    cfsetospeed( &tio, baudRate );

    cfmakeraw(&tio);                    // RAWモード

    tcsetattr( fd, TCSANOW, &tio );     // デバイスに設定を行う

    ioctl(fd, TCSETS, &tio);            // ポートの設定を有効にする

    write(fd, msg, sizeof(msg));    // デバイスへ255バイト書き込み
    printf("%s", msg);

    // 送受信処理ループ
    while(1) {
        len = read(fd, buf, sizeof(buf));
        if (0 < len) {
            for(i = 0; i < len; i++) {
                printf("%02X", buf[i]);
            }
            printf("\n");
        }

        // エコーバック
        write(fd, buf, len);
    }

    close(fd);                              // デバイスのクローズ
    return 0;
}

Raspberrypi のデバイスファイルについて

前回の記事でも書きましたが、Raspberrypiのシリアルポートのデバイスファイルは "/dev/serial0"としてマッピングされています。
手元のRaspberrypiだと/dev/ttyS0だとopenは成功しましたが、うまく通信できませんでした。ご注意ください。

結線は以前の記事でも書きましたが、
6 :GND
8 :TXD
10:RXD
になります。

感想

組み込みソフトのプログラミングだとシリアル通信のプログラミングは比較的簡単な部類に入ります。
UARTのデバイスドライバは割り込みハンドラを入れてもC言語で200~300行程度にしかなりません。
UARTは各種デバイスやセンサなどとのインターフェースとして使われますが、「端末」との通信のように制御文字処理する必要はありません。
今回termiousを使ってみてその辺の作法がよくわからず少し苦労しました。

現代ではサーバへのログインアクセスはもっぱらSSHになりました。
RS-232Cを使ったシリアルコンソールログインなどは組み込みLinuxを使う時くらいしかありません。
(SSHと違ってシリアルコンソールだとviがうまく動かなかったりするんですよね...)

そのためか「termios/端末プログラミング」について触れられている書籍はあまりありません。

いろいろと本棚の書籍やKindleをあさっていると、何と詳解Unixプログラミングには「端末入出力」の章があるではありませんか!サンクス、Mr.スティーブンス.

詳解UNIXプログラミング 第3版

詳解UNIXプログラミング 第3版

詳解Unixプログラムは分厚くて机に置いておくと邪魔になるので普段めったに読まない本ですが、こういうときに役に立つんですね。Unix/Linux系のコードを書くときはやはりありがたい一冊です。

ちなみに、Linuxプログラミングインターフェースという本でも端末プログラミングについて解説されている章があるようです。こちらの本は読んでみたいのですが、ちょっと高いな・・・

Linuxプログラミングインタフェース

Linuxプログラミングインタフェース

termiousを使うプログラムはUnixとしての「端末プログラミング」の作法を押さえておく必要がありそうです。

あと、よく考えてみればRubyのserialportがRaspberry pi上で動いているので、素直に拡張ライブラリのコードを読んでみるというのも1つの手段だと思いました。時間があるとき読んでみたいと思います。

farポインタとポインタの違い

RL78マイコンを使っていてポインタと__farのついたポインタサイズの違いを意識する必要がありましたので書いておきたいと思います。

結論

__far付きのポインタと(__farのつかない)ポインタでは型のサイズが異なる場合があり、その場合アクセスできる領域も当然違ってきます。(ここでの __farはRL78のコンパイラ(CS+用)でのキーワードです)

そもそも far とは何か?

16bit・32bit・64bitなどCPUのビット幅の違いとして、使用できるメモリの容量をよく耳にすることがあると思います。

パソコンだと、
「32bit版のWindowsだとメモリは4GBまでだけど、64bit版だと4GB以上メモリを搭載できる」

という話は皆さんご存知かと思います。

32bitで表現できる値は0~4294967295になりますのでアドレス空間に換算すると4GBの領域を表現できることになります。
64bitですと、0~18446744073709551616になり16EB(エクサバイト)まで表現可能です。
実際には16EBまでをアドレス空間として認識するOSはないと思いますが。

では、16bitではどうでしょうか?
16bitで表現できるアドレスは、0~65535 になりますので単純にアドレス空間に換算すると64KBになります。

しかしながら世の中に存在する16bitのCPUにはアクセスできるアドレス空間は1MBというCPUがよくあります。
有名どころですと、Intelの8086も1MBのアドレス空間を利用可能ですし、取り上げているRL78も1MBのアドレス空間にアクセス可能ですし、同じくルネサスのM16Cも16bitで1MBのメモリ空間にアクセス可能です。

1MBのアドレス空間が利用できると言っても、

16bitでどうやって64KBより大きなアドレスを表現するのか?64KBが上限じゃないのか?

という疑問が湧いてくると思います。

セグメントレジスタ

では、どのようにして16bitCPUが1MBのアドレス空間にアクセスしているかというと、RL78もIntel8086もセグメントレジスタというレジスタがあり、このレジスタを使って64KBより大きいアドレスにアクセスすることが可能になります。

具体的には、セグメントレジスタのうち4bitを上位アドレスとして使うことで64KBより大きいアドレスへのアクセスを可能にしています。

要するにアドレスを決めるためのレジスタを2個使う(16bit×2レジスタ)というのが、16bitCPUで64KBを超えるアドレスへのアクセスのからくりです。

farポインタとポインタでは変数のサイズが違う

一般的にはポインタ変数のサイズCPUのレジスタのサイズによって決まります。

例えば、ポインタサイズを確認するC言語のコードは以下のようなコードになります。

#include <stdio.h>

int main(int argc, char **argv) {
	int ptr_size;

	ptr_size = sizeof(int *);

	printf("ptr_size:%d\n", ptr_size);

	return 0;
}

32bit環境であれば、「ptr_size:4」と表示されますし、64bit環境であれば「ptr_size:8」と表示されます。

※64bit OS上でビルドしても、ビルドの設定が32bit用になっていれば「ptr_size:4」と表示されますので注意してください。

C言語からはレジスタに直接アクセスできない!

さて、セグメントレジスタを使うことで16bitでは表現しきれないアドレスの表現ができることは理解して頂けたかと思いますが、実際にC言語でそのようなコードを書く場合はどうなるのでしょうか?

例えば0X0FFFFFに 1234を書き込むコードは以下のようなコードになります。

    *((int *)(0X0FFFFF)) = 1234;
    

のようになります。

ところが、このコードは16bitCPUでポインタ型2byteの場合、警告がでて、そのままでは意図した結果になりません。

ポインタ型が2byteであればアクセス先は0xFFFFに丸められてしまいます。

0x0FFFFFにアクセスする場合、
セグメントレジスタには、0x000Fを設定してあげる必要があるのですが、C言語にはCPUのレジスタにアクセスする手段はありません。
※厳密にはインラインアセンブラの機能を持つコンパイラであれば可能ですが。

さて、ここでタイトルの通り、farポインタ(__farキーワード)の登場です。

RL78コンパイラで上記の例をfarポインタアクセスする場合、

    *((int __far *)(0X0FFFFF)) = 1234;

という書き方になります。

__farをつけることで、コンパイラに対し、

「これは64KBを超えるアドレスへアクセスしたいんだからね!アドレッシング気を付けてね!」
「ちゃんとセグメントレジスタを使ってね」

と要求することになります。

上記の__far というキーワードはあくまでRL78コンパイラによって定義されているものですのでパソコン上のコンパイラ(GCC等)ではコンパイルエラーになります。

まとめ

現代のプログラマーは、普段プログラミングをしている限り、farポインタ について理解しておく必要はあまりないと思います。

しかし、組み込みソフトの開発で使われるマイコンには今でも16bit CPU/1MBアドレス空間のようなCPUがあります。
ただし、そのようなCPUであっても普通にプログラミングをしている場合はポインタのサイズやアクセス先がメモリ空間のどこに位置しているかを意識する必要はありません。

意識する必要が出てくるのは、上記のコードのようにアドレスを直接指定するようなコードを書くときぐらいでしょうか。

インテル8080伝説

インテル8080伝説

はじめて読む8086―16ビット・コンピュータをやさしく語る (アスキーブックス)

はじめて読む8086―16ビット・コンピュータをやさしく語る (アスキーブックス)

abrAsusの小さい財布を買ってみた

abrAsusの小さい財布を買ってみました。

財布を買うのは数年ぶりなのと、今までに使ったことのないタイプの財布なのでテンションが上がったので記事として書いておきたいと思いました。

買い換えた経緯

私はいつも財布をズボンの右後ろのポケットに入れているのですが、先週1週間で2回ズボンから落としてしまいました。
今まで使っていた財布はかれこれ5年程使っていたのですが、長年の使用による経年劣化のためか表面がツルツルになっており、何かの拍子で落としやすい状態になっていたようです。

1回目に落とした時は、近くにいた方が指摘してくださったのですが、2回目は仕事中にオフィスのトイレに行った際に落としたようで、自分でも気付かず、隣のオフィスの方がビル管理人の方に届けてくださいました。

1週間に2回落としたことにショックを受け、根本的に財布の持ち方について改める必要があると考えて新しい財布を購入することにしたという次第です。

選定条件

そもそも、これまでにも何度か財布を落としたことがあり、財布の状態以前に、ズボンの後ろポケットに入れていることが落とすことの要因になっていると感じましたので次に買う財布については、ズボンの前側のポケットに入れるということを前提に考えました。

ちなみにズボンの後ろのポケットに財布を入れている人って結構いると思うんですけど、実際のところどうなんでしょう。同じように財布をよく落とす方の参考になればと思います。

さて、前ポケットに入れるとなるとそれなりに小さい財布であることが条件になるのですが、以前に「薄い財布~GoogDesign賞受賞~」という項目をみたことを覚えていたので、この薄い財布というが真っ先に候補に挙がっていました。

広告を見る限り、薄くて使いやすいという印象を受けましたが実際に使った人のレビューなどを見ていると小銭の保存具合はあまりよくないという評価も結構見受けられました。

薄い財布のabrAsusからはいくつか財布が販売されているのですが、今回購入した「小さい財布」は、薄い財布と比べると大きさや厚さは大きくなりますが、比較的レビューの評価もよく、私の目的としている用途に合っている要でしたので購入してみました。

続きを読む

高知県のおすすめスポットについて

このブログのアクセス内容を見ていると、去年(2016年)、高知県の栢島に行った時のことを書いた記事を最近よく閲覧頂いているようです。

mcommit.hatenadiary.com


高知県出身者としてはとてもありがたく感じております。
栢島はとてもきれいで、楽しい時間が過ごせたので今年もできれば行きたいと思います。

皆さんにもぜひ栢島に行って頂きたいという気持ちと同時に、せっかくなので高知県のほかのおすすめスポットについても一応ですが「地元民」として紹介させて頂ければと思います。

私の大雑把な主観ですが、高知市(高知駅周辺)からの移動時間の目安も記載しておきます。それほど的外れな時間感覚ではない自信はありますが、渋滞してると時間がかかる場合もあると思いますのであくまで目安としてお考え下さい

  • 秦山公園
  • 国虎屋
  • 日ノ御子河川公園キャンプ場
  • ヤシィパーク
  • 番外編(おすすめのお土産)
  • まとめ

秦山公園

f:id:simotin13:20170610014743j:plain

子供連れのファミリーにはぜひおすすめの公園です。
私も帰省した際にはいつも行っています。遊具も多く、人気の公園なので祝日や休みの日は結構にぎわっていますが、混んでるからといって遊べないということはないと思います。
駐車場(無料)も2か所ほどあり、私の経験では満車になっていたことはありません。

www.city.kami.kochi.jp

大きな滑り台とトランポリンが見所です。
他にも遊具がたくさんありますので小学生低学年までの小さいお子さん連れの方はぜひ遊びに行ってみてください。

移動時間の目安:30分~40分(高知市から)

続きを読む

HHKB Professional BT(日本語配列)を1か月使ってみた

1か月ほど前に購入したので使用感を書いておきたいと思います。

購入のきっかけ

BluetoothタイプではないHHKBのProfessional JPをここ5年くらい使っています。

普段使っているPCはノートPCだったので優先のキーボードで特に問題はなかったのですが、最近デスクトップ型のPCを買うことになり合わせてBluetoothタイプのHHKBが欲しくなりました。

多くの方がそうだと思いますが、デスクトップPCは机の下(足元)に置く形になります。

その場合、キーボードのUSBケーブルがそれなりに長いケーブルが必要になるのですが、机の上にはそれでなくてもUSBケーブルや基板やら、LANケーブルやらはんだごてなどが居座っているので少しでもすっきりさせたいという思いで購入しました。

よかった点

基本的にはHHKBと同じなので今更あえてHHKBの良さを語るのはやめておきたいと思います。

上記の

机の上をすっきりさせたい!

という目論見は大成功で、気持ちよく作業ができます。

「USBケーブルがいらない」

というBluetoothのメリットが最大限生かせているのでいい感じです。

いまいちと感じた点

非常に満足しているBluetoothタイプのHHKBですが、1点想定外のデメリットというか困った点がありました。

それは、

長時間キーボードを放置した場合、キーボードの電源スイッチを長押ししなければならない

という点です。
Bluetooth機器なので長時間放置された場合は節電のために自動的に電源が切れるようになっています。

再度復帰させる場合はキーボード本体のスイッチを長押ししてあげる必要があります。
毎回PCを使うたびにこの作業が必要になるのですが、これが地味に面倒です。(スイッチも小さいので押しやすくはないですし)

スイッチの長押しではなくて特定のキーボードを何回か押したらとかだったらなぁ・・・と思います。
(例えばエンターキー3回とか)

しかし、よくよく考えてみるとHHKB以外にもBluetoothタイプのキーボードはあるのですが今まで使ったことがなかったです。他のBluetoothタイプキーボードも同じようなスイッチとか押す必要があるんだろうか・・・

HHKBを使う理由

私がHHKBを使う理由は単純で、

  1. 「ctrl」キーが通常のキーボードの「caps lock」の位置にある。(所謂UNIX配列)
  2. キータッチが軽い

の2点です。

そういえば同じ理由でMacbookを使っています。
エディタでemacsを使ったりターミナルで「ctrl」キーをよく使うので「ctrlキー」は「A」の横にあってほしいというシンプルな理由です。

どのOSでもソフトで切り替えたりもできますが、そういったことをいちいちする手間がないというだけでとてもありがたいです。

英語配列日本語配列か?

キーボードに対するこだわりとして「英語配列」がいいという方がいらっしゃいますが、私はまだ英語配列のキーボードを触ったことがありません。

日本語を扱う以上、「半角/全角」の切り替えが必ず必要になりますが英語配列の場合複数キーの組み合わせで切り替えをする必要があるようで、なんとなくそれが使いにくそうだなぁという印象があり日本語配列のキーボードを使っています。

英語配列は慣れると生産性が高いという声をよく聞くので機会があれば触ってみたいものです。

感想

HHKBはとても使い勝手のよいキーボードですのでこれからもずっと愛用していくことになりそうです。
両方のタイプを使ってみた感想として、同じHHKBとして比較する場合、BluetoothタイプもUSBケーブルもそれぞれメリットデメリットがありますので一概にどちらがいいとは言えないかと思いました。

作業する机の上の状況や使用するPCのタイプに合わせて検討してみるとよいかと思います。


/*~*/ コメントアウトを除去するコードをRubyで書いてみた

タイトルの通り、C言語系の複数行 /* ~ */ コメントを除去するコードを書いてみました。
Rubyで書いていますが状態遷移が基本になるのでどの言語でも同じように書けそうです。

# コメントアウトの除去
def delete_comment_out code
  status = :STATUS_WAIT_START_SLASH
  tmp = ""
  comment_out_code = ""
  code.each_char do |c|
    case status
    when :STATUS_WAIT_START_SLASH
      if (c == '/')
        tmp = c
        status = :STATUS_WAIT_START_ASTR
      else
        comment_out_code << c
      end
    when :STATUS_WAIT_START_ASTR
      if (c == '*')
        status = :STATUS_WAIT_END_ASTR
      else
        status = :STATUS_WAIT_START_SLASH
        comment_out_code << tmp
        comment_out_code << c
      end
    when :STATUS_WAIT_END_ASTR
      if (c == '*')
        status = :STATUS_WAIT_END_SLASH
      end
    when :STATUS_WAIT_END_SLASH
      if (c == '/')
        status = :STATUS_WAIT_START_SLASH
      end
    end
  end
  if status != :STATUS_WAIT_START_SLASH
    puts "status error"
  end
  comment_out_code
end

# テスト
code = File.read("test.c")
puts "--------------- before ------------------"
puts "#{code}"

comment_out_code = delete_comment_out(code)

puts "---------------  after ------------------"
puts "#{comment_out_code}"

元コード

#include <stdio.h>

// スラスラコメントは消えないよ
float func(void)
{
  float f = 1 * 2 / 3;
  int n = 1*2/3;

  /*
   * 複数行コメントも大丈夫
   */

   /* 掛け算・割り算も大丈夫 */
   return 1 * 2 / 3;
}

変換後コード

#include <stdio.h>


// スラスラコメントは消えないよ
float func(void)
{
  float f = 1 * 2 / 3;
  int n = 1*2/3;

  

   
   return 1 * 2 / 3;
}

状態遷移表

正規表現でもできると思っていたのですが、複数行でのマッチングがうまくできなかったのでコードで書いてみました。これくらい何も考えなくても10分くらいあれば書けるだろうと思っていたのですが、意外とパッと手が動かなかったので、まじめに状態遷移表を書いてからコードに落としてみました。

f:id:simotin13:20170513012803p:plain

感想

C言語の場合、コメント行の除去処理はプリプロセッサの仕事ですが、こうしてコードを書いてみると*/がない場合のエラー検出はコンパイル前に検出できますね。当たり前と言えば当たり前なんですが「コンパイルエラー」がどこで検出できるかまではあまり意識していないのでなんか新鮮な気持ちになりました。