Apache Arrowのビルド
先日OSS Gate東京ミートアップ for Red Data Tools in Speeeに参加して、Apache Arrowの開発にデビューしました。自分の専門でない分野に一人で飛び込むのはなかなか大変なので、こういうイベントがあるのは助かります。
と言っても2時間の中でできたのは、公式サイトのミスの修正案のPullRequestを送ったくらいで(無事マージされました)、その後Arrowをビルドにトライし、cpp (Arrow C++) はすぐにできたものの、c_glib (Arrow C++のglibラッパー) ビルドの途中でいくつかエラーがあり、時間切れという感じでした。
その後家で続きをやったのでまとめ。試したのはUbuntu-16.04とmacOS Sierraで、Arrowはそのときgithubから持ってきたもの(5cda6934999f9f79368f3fc3f68895fc0f4e0b24)です。
Ubuntu-16.04
cpp
cppのビルドはcpp/README.mdの手順通りで問題なし。ただし、ビルドだけでなくsudo make installしておかないとc_glibのビルドに失敗する。また、初めてインストールしたときはsudo ldconfigしておかないとやはりc_glibのビルドに失敗する。
% git clone https://github.com/apache/arrow.git % cd arrow/cpp % mkdir debug % cd debug % cmake .. % make unittest % sudo make install % sudo ldconfig
c_glib
ビルド手順はc_glib/README.mdに載っている。今回はgithubから取ってきたソースなので、「How to build by users」ではなく「How to build by developers」に従ってビルドする。
c_glibのビルドもほぼc_glib/README.mdの手順通り。
% cd c_glib % ./autogen.sh % ./configure % make % sudo make install
なお、以下のようなエラーが出た場合は多分cppビルド後のmake install, ldconfigができていない。
make[3]: ディレクトリ '/home/wagavulin/arrow/c_glib/arrow-glib' に入ります GISCAN Arrow-1.0.gir /home/wagavulin/arrow/c_glib/arrow-glib/tmp-introspectxbzARl/.libs/lt-Arrow-1.0: error while loading share$ libraries: libarrow.so.0: cannot open shared object file: No such file or directory
macOS Sierra
cpp
cpp/README.mdの通りだが、ビルド後にsudo make installする。なお、Linuxとは異なりldconfigは必要ない(macOSにはldocnfig自体ない)。
c_glib
基本的にはc_glib/REAMDE.mdの「How to build by developers」に従ってビルドするが、いくつかトラブった。
AX_CXX_COMPILE_STDCXX_11マクロの問題
configureを実行したところ途中で失敗。原因はAX_CXX_COMPILE_STDCXX_11マクロに関するもの。調べてみると、このマクロを使用するにはautoconf-archiveを入れる必要があるらしいが、すでに手元のマシンには入っている。試しにアンインストールしてもう一度インストールしたら以下のようなメッセージが出ていた。
$ brew install autoconf-archive Warning: autoconf-archive 2017.03.21 is already installed, it's just not linked. You can use `brew link autoconf-archive` to link this version.
どうやらインストールはされたもののリンクが作られていないため見つけられない状態のようだ。メッセージに従ってbrew link autoconf-archiveすると今度は以下のようになった。
$ brew link autoconf-archive Linking /usr/local/Cellar/autoconf-archive/2017.03.21... Error: Could not symlink share/aclocal/ax_check_enable_debug.m4 Target /usr/local/share/aclocal/ax_check_enable_debug.m4 is a symlink belonging to gnome-common. You can unlink it: brew unlink gnome-common To force the link and overwrite all conflicting files: brew link --overwrite autoconf-archive To list all files that would be deleted: brew link --overwrite --dry-run autoconf-archive
リンクが作られていないのはgnome-commonというパッケージと衝突するかららしい。仕方ないのでbrew unlink gnome-commonした後brew link autoconf-archiveしたら成功し、AX_CXX_COMPILE_STDCXX_11マクロに関するエラーも解決した。
なお、gnome-commonを入れてなければautoconf-archiveインストール時に自動的にリンクが作られているので、この問題に遭遇することはなさそう。
libffiの問題
AX_CXX_COMPILE_STDCXX_11のエラーは解決したが、今度はgobject-introspectionがないと言われた。
checking for gobject-introspection... configure: error: gobject-introspection-1.0 is not installed
gobject-introspectionは入ってるはずなんだが...。config.logを見てみると、gobject-introspectionが見つからないのではなく、libffiが見つからないのが原因のようだ。
configure:16847: checking for gobject-introspection configure:16856: $PKG_CONFIG --exists --print-errors "gobject-introspection-1.0" Package libffi was not found in the pkg-config search path. Perhaps you should add the directory containing `libffi.pc' to the PKG_CONFIG_PATH environment variable Package 'libffi', required by 'gobject-introspection-1.0', not found configure:16859: $? = 1 configure:16863: error: gobject-introspection-1.0 is not installed
libffiはkeg onlyというやつで、インストールしてもリンクが作られないためpkg-configが見つけられない。brew linkに--forceを付けて強制的にリンクすることもできるようだが、どういう副作用があるか分からない。pkg-configの探索パスに追加するだけでも回避できるようなのでその方向でやろう。
$ export PKG_CONFIG_PATH="/usr/local/Cellar/libffi/3.2.1/lib/pkgconfig" $ ./configure
これでビルドできた。ただし、ビルドの途中で以下のようなエラーがずらずらと出てきた。
GISCAN Arrow-1.0.gir /usr/include/signal.h:79: syntax error, unexpected identifier, expecting ')' in 'void (* _Nullable bsd_signal(int, void (* _Nullable)(int)))(int);' at 'bsd_signal' /usr/include/signal.h:79: syntax error, unexpected ')', expecting ',' or ';' in 'void (* _Nullable bsd_signal(int, void (* _Nullable)(int)))(int);' at ')' 以下略
@kouさんによると問題なさそうで、test/run-test.shが動けばよさそう。やってみたところ100% passedになった。
ということでようやくビルド成功という感じです。
CMakeを使ってみた (7) find_packageとpkg_check_modulesによるライブラリ探索
久しぶりにCMakeの話。
外部の依存ライブラリがあるC/C++のコードをCMakeでビルドする場合、インクルードパスやライブラリパスを指定する必要がある。パスを直接指定する方法以前書いた。しかし、そこで書いたのはパスやライブラリ名を直接指定するもので、それらがすでに分かっている必要がある。
しかし、システムにインストールされたライブラリを使うような場合はそのパスを探し出して指定する必要がある。システムによってインストールされた場所が異なることがあるためだ。Unix系OSでよく使われるGNU Autotools環境では、ライブラリの探索はAutoconfの役目で、`./configure && make`の`./configure`スクリプトが行う。では同様のことをCMakeでやるにはどうすれば良いだろうか。
ということで今回はCMakeのライブラリ探索の話。方法はいくつかあるので、順番に見ていこう。
find_packageコマンドを使う
CMakeをインストールするとcmakeコマンドだけでなくたくさんのモジュールをインストールしており、よく知られたライブラリの探索モジュールも含まれている。利用可能なモジュールの一覧は`cmake --help-module-list`で表示できる。多数のモジュールが表示されるが、その中でFindXXXという名前になっているのが探索用のモジュールだ。
ここにあるライブラリであればfind_packageコマンドを使って探すことができる。まずはこれを使ってみよう。
例としてGTK2を使うアプリを考える。以下のような、GTK2を使うソースファイル (main.c) を作った。GTK自体の実験ではないので単にgtk_init()を呼び出すだけで何もしないプログラムだが、ビルドにはインクルードパスなどの指定が必要だ。
#include <gtk/gtk.h> int main(int argc, char **argv){ gtk_init(&argc, &argv); }
これをビルドするCMakeLists.txtは以下のようになる。
cmake_minimum_required(VERSION 2.6) project(Hello C) add_executable(hello main.c) find_package(GTK2 REQUIRED) include_directories(${GTK2_INCLUDE_DIRS}) target_link_libraries(hello ${GTK2_LIBRARIES})
find_packageコマンドを実行すると、指定したライブラリの探索モジュール(FindGTK2)を探し、それを実行する。FindGTK2はそのシステムにあるGTK2を探し、その結果を変数に入れる。この場合、インクルードパスやリンクするべきライブラリがGTK2_INCLUDE_DIRSとGTK2_LIBRARIESにセットされる。あとはinclude_directoriesやtarget_link_librariesを使って指定すればよい。なお、このプログラムにとってGTK2は必須のライブラリなのでREQUIREDを付けている。
find_packageコマンドがセットする変数
モジュールXXXに対し、find_packageがセットする変数はだいたい以下のようになる。意味は見れば分かるだろう。
- XXX_FOUND
- XXX_INCLUDE_DIRSまたはXXX_INCLUDES
- XXX_LIBRARIESまたはXXX_LIBS
- XXX_DEFINITIONS
XXXの部分はGTK2_FOUND, CURSES_FOUNDなどのように全て大文字であることが多いが、Boost_FOUNDなどのようにキャメルケースになっている場合もある。具体的にどのような変数がセットされるかは、cmake --help-module FindGTK2 とすれば分かる。
find_packageの動作の詳細
find_packageの内部動作には2種類あり、モジュールモードとコンフィグモードと呼ばれている。
find_package(XXX)を呼び出すと、cmakeはまずFindXXX.cmakeというファイルを探す。最初に${CMAKE_MODULE_PATH}で指定されたディレクトリを探し、なければ
FindXXX.cmakeを使うのがモジュールモードで、XXXConfig.cmake/xxx-config.cmakeを使うのがコンフィグモードだ。先の例ではFindGTK2.cmakeを使ったので、モジュールモードを使ったことになる。
モジュールモードとコンフィグモードの違いは単なる優先度の差だけではなく、作成者と処理の内容が(通常は)異なる。モジュールモードは、そのファイル名であるFindXXXという名前が示す通り、指定したライブラリを探すものであり、つまりそのライブラリの作成/インストールした人間以外が書くものだ。
一方コンフィグモードのファイルは探す対象のライブラリ自身によって置かれることを想定している。多分そのライブラリのインストーラなどが置く場合だろう。そのため、XXXConfig.cmake/xxx-config.cmakeは通常はライブラリを「探す」ことはしない。そのライブラリ自身が置いたのであれば、ライブラリがどこにインストールされたのかは知っているはずなので、単にハードコードされた値が書かれているだろう。
ただし、現状はコンフィグモードの.cmakeを置くライブラリはあまりないそうだ。実際、手元のUbuntu-16.04を見てもそのようなファイルは見当たらない。そういうわけで、コンフィグモードを使うのはCMakeがもっと普及したら、あるいは自分で作る場合だけになりそうだ。
pkg_check_modulesを使う
find_packageが使えない場合は探索作業を自分で行うことになる。例えば、手元の環境のcmakeにはFindGTK(GTK1用)とFindGTK2(GTK2用)はあるが、GTK3を探すモジュールがない。仕方ないのでGTK3を自分で探すわけだが、pkg_check_modulesが使えるならそんなに難しくない。
とここでpkg_check_modulesの説明の前に、それが内部で使うpkg-configを説明する。
pkg-configとは
pkg-configはインクルードパスやライブラリパスといった、そのライブラリを使うアプリケーションをビルドするのに必要な情報を提供してくれるツールだ。freedesktop.orgという、Unix系OSのデスクトップ環境の共通仕様・ツールを提供する団体が作ったものらしい(なのでpkg-config自体はCMakeとは無関係)。Linux, *BSD, Mac OS X, WindowsのMSYSといった多くの環境で使えるため、これを使えば簡単にポータブルなビルド環境を作ることができる。
使い方の詳細はmanを見て欲しいが、`pkg-config --cflags
$ pkg-config --cflags gtk+-3.0 -pthread -I/usr/include/gtk-3.0 -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -I/usr/include/gtk-3.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/mirclient -I/usr/include/mircommon -I/usr/include/mircookie -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/harfbuzz -I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/libpng12 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/libpng12 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include $ pkg-config --libs gtk+-3.0 -lgtk-3 -lgdk-3 -lpangocairo-1.0 -lpango-1.0 -latk-1.0 -lcairo-gobject -lcairo -lgdk_pixbuf-2.0 -lgio-2.0 -lgobject-2.0 -lglib-2.0
GTK3を使うアプリなら以下のようにすればビルドできる。
$ gcc `pkg-config --cflags gtk+-3.0` main.c `pkg-config --libs gtk+-3.0`
なお、指定可能なライブラリ一覧は`pkg-config --list-all`で表示できる。
CMakeでのpkg-configの利用
で、この便利なpkg-configをCMakeから使えるようにしたのがPkgConfigモジュールだ。使い方はサンプルを見れば分かるだろう。
cmake_minimum_required(VERSION 2.6) project(Hello C) add_executable(hello main.c) find_package(PkgConfig REQUIRED) pkg_check_modules(GTK3 gtk+-3.0 REQUIRED) include_directories(${GTK3_INCLUDE_DIRS}) target_link_libraries(hello ${GTK3_LIBRARIES})
まずはfind_packageでこのモジュールを見つける。実際にpkg-configを使うのはpkg_check_modulesコマンドで、pkg_check_modules(
見ての通り、FindXXXを使った場合とたいして違いはない。実際、デフォルトでインストールされるFindXXX.cmakeも内部ではpkg_check_modulesを使っているものもある。
FindXXX.cmakeをコピーして使う
find_packageが使えない場合のもう1つの解決法として、他人が作ったコマンドをコピーして使うというのがある。ということでネット上を探してみる。
あるとすれば恐らくFindGTK3.cmakeという名前なので、その名前でググってみるといくつかそれっぽいのもが出てくる。中でもChromiumに含まれているものなら信頼できそうな感じがするので、これをコピーしてFindGTK3.cmakeという名前で保存しよう。
(念のため書くが、コードをパクるときはライセンスを確認しよう。とは言っても、出来上がるバイナリには含まれないビルドツール用のコードをコピーした場合はどうなるんだ?)
とりあえず、プロジェクトのトップにcmakeディレクトリを作り、その中に置いてみた。
test/ +- CMakeLists.txt +- main.c +- cmake/ +- FindGTK3.cmake
CMakeLists.txtは以下のようになる。
cmake_minimum_required(VERSION 2.6) project(Hello C) add_executable(hello main.c) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") find_package(GTK3 REQUIRED) include_directories(${GTK3_INCLUDE_DIRS}) target_link_libraries(hello ${GTK3_LIBRARIES})
自分で置いたFindXXX.cmakeを使う場合、CMAKE_MODULE_PATHを指定する必要がある。上の方にも書いたとおり、モジュールの探索はCMAKE_MODULE_PATHとCMakeがインストールされたディレクトリのModules以下のみで、たとえCMakeLists.txtと同じディレクトリに置いたとしても自動的に読んではくれない。
REQUIREDとQUIET
find_package, pkg_check_modules共通のオプションにREQUIREDとQUIETがある。find_packageやpkg_check_modulesにREQUIREDを指定しないと、見つからなかった場合にメッセージは出すが処理は続行する。REQUIREDを指定すると見つからなかった場合は処理がそこで止まり、cmakeコマンド自体の戻り値も1になる。また、QUIETを指定すると見つからなかった場合のメッセージ出力が抑制される。
なお、find_packageにREQUIREDを指定しなかった場合でも、FindXXX.cmake自体が見つからなかった場合はエラーとなり、そこで処理は中止される。
以下に例を挙げる。なお手元の環境にはQt4が入っていない。
find_package(PkgConfig REQUIRED) pkg_check_modules(XXX xxx) # xxxというモジュールはないためエラーメッ # セージが出力されるが処理は続行 pkg_check_modules(XXX xxx QUIET) # エラーメッセージを出さずに続行 pkg_check_modules(XXX xxx REQUIRED) # エラーとなり処理が中断される
find_package(Qt4) # エラーメッセージを出力して処理続行 find_package(Qt4 QUIET) # エラーメッセージを出さずに続行 find_package(Qt4 REQUIRED) # エラーとなり処理が中断される
find_package(YYY) # FindYYY.cmakeがないため、REQUIREDがついて # いなくてもエラー終了
参考資料
- CMake:How To Find Libraries
- https://cmake.org/Wiki/CMake:How_To_Find_Libraries
- find_packageの公式マニュアル
- https://cmake.org/cmake/help/v3.0/command/find_package.html
- pkg_check_modulesの公式マニュアル
- https://cmake.org/cmake/help/v3.0/module/FindPkgConfig.html
C++のマングルとextern "C" {
C++とCが混在したプログラムを書くとときどき定義したはずの関数がundefinedだと言われることがある。そんなときの対処法とマングルの話。
その前にまずはC言語だけの場合を考える。例えば以下のようなCのプログラムを書いてみる。
/* main.c */ #include "foo.h" int main(){ func1(); return 0; }
/* foo.h */ #ifndef FOO_H void func1(); #endif
/* foo.c */ #include "foo.h" #include <stdio.h> void func1(){ puts("ok"); }
見てのとおり、main.cはfunc1関数を呼び出しており、foo.cはfunc1関数を定義している。当然、main.cをコンパイルしてできたmain.oもfunc1を参照し、foo.cをコンパイルしてできたfoo.oはfunc1を定義する。
オブジェクト中で使われているシンボルはnmコマンドで見ることができる。
$ gcc -c main.c $ nm main.o U func1 0000000000000000 T main $ gcc -c foo.c $ nm foo.o 0000000000000000 T func1 U puts
"U"になっているのは定義されていないもの、つまりそのシンボルを外部参照している。"T"になっているのはそのシンボルが定義されていることを意味する(正確には、"T"は「そのシンボルがテキスト(コード)セクションにある」という意味だが、細かいことは今回は関係ないので省略)。
このため、main.oとfoo.oをリンクさせてやれば、func1関数の呼び出しが可能になる。
$ gcc main.o foo.o $ ./a.out ok
C++の場合
C++の場合もCと同じような方法でシンボルの解決を行うが、Cとは違ってソースコード中の関数/変数名をそのままオブジェクトファイル中のシンボルにすることはできない。というのは、C++では同名の関数を複数作ることができるからだ。引数の型が異なれば同じ名前を使えるし(オーバーロード)、クラスや名前空間が異なればやはり同じ名前を使える。以下のような場合を考えよう。
// main.cpp #include "foo.h" int main(){ func1(); func1(10); Foo::func1(); }
// foo.h #ifndef FOO_H #define FOO_H void func1(); void func1(int); class Foo { public: static void func1(); }; #endif // FOO_H
// foo.cpp #include "foo.h" void func1(){} void func1(int){} void Foo::func1(){}
このソースでは、func1という名前の関数が3つ存在する。そのため、func1という名前だけでは同定することができず、正しくリンクできなくなってしまう。そのため、C++コンパイラは、引数の型やクラス・名前空間名を使って修飾された一意な名前を作成し、オブジェクトファイルにはその修飾された名前を使う。これをマングル(mangle)と呼ぶ。
実際にコンパイルして見て見ると以下のようになる。
$ g++ -c main.cpp $ nm main.o U _Z5func1i U _Z5func1v U _ZN3Foo5func1Ev 0000000000000000 T main $ g++ -c foo.cpp $ nm foo.o 0000000000000007 T _Z5func1i 0000000000000000 T _Z5func1v 0000000000000012 T _ZN3Foo5func1Ev
なにやら複雑な名前になっているが、よく見ると"func1"や"Foo"という文字が見える。なお、マングルされた名前から元に戻すことをデマングルと呼び、nmコマンドでは--demangleでデマングルできる。
$ nm --demangle main.o U func1(int) U func1() U Foo::func1() 0000000000000000 T main $ nm --demangle foo.o 0000000000000007 T func1(int) 0000000000000000 T func1() 0000000000000012 T Foo::func1()
C++からCの関数を呼ぶ場合
C++からCの関数を呼ぶ場合、このマングル処理が問題になる。以下の例を考えよう。
// main.cpp #include "foo.h" int main(){ func1(); }
/* foo.h */ #ifndef FOO_H #define FOO_H void func1(); #endif // FOO_H
/* foo.c */ #include "foo.h" void func1(){}
main.cppはC++、foo.cはCで書かれている。これをビルドしてみよう。
$ g++ -c main.cpp $ gcc -c foo.c $ g++ main.o foo.o main.o: In function `main': main.cpp:(.text+0x5): undefined reference to `func1()' collect2: error: ld returned 1 exit status
と、こんな風にエラーになってしまった。エラーメッセージによれば、func1()が見つからないらしいが、それはfoo.oにあるはずだ。
リンクできない理由はnmを使えば分かる。
$ nm main.o U _Z5func1v 0000000000000000 T main $ nm foo.o 0000000000000000 T func1
main.oはC++コンパイラが作ったので関数名がマングル,され"_Z5func1v"を参照しているが、foo.oはCコンパイラが作ったのでマングルされず、"func1"になっているのだ。
これを解決するには、C++コンパイラに対して、「func1はCの関数だからマングルしない名前で参照せよ」と命じる必要がある。これを行うのがextern "C" { ... }だ。これで囲まれた中で宣言された関数はCの関数とみなされ、マングルされずに使われる。従って、foo.hにある宣言をexter "C" { ... }で囲めば良い。
ただし、この構文はCコンパイラは理解できずエラーになる。そのため、__cplusplusマクロを使い、C++コンパイラから読まればときだけ有効になるようにする。
/* foo.h */ #ifndef FOO_H #define FOO_H #ifdef __cplusplus extern "C" { #endif void func1(); #ifdef __cplusplus } #endif #endif // FOO_H
これでようやくビルドできる。このextern "C" { ... }とインクルードガードはCのヘッダには必ず入れるようにしよう。
Cのヘッダにextern "C" {}がない場合
Cのヘッダにextern "C" {}が書かれておらず、かつそれが他人の作ったやつで変更できないような場合は、include文を囲むことで回避できる。上の例でfoo.hがC++対応していない場合、main.cppのinclude文を
extern "C" { #include "foo.h" }
とすれば良い。
Emacsで制御コードを入力する
Emacsを使っているときでも時々制御コードを直接入力したいことがある。
- 文字列を置換するときに改行を入れたい
- 強制的にタブを入力したい(たいていのモードではタブはインデントの設定になる)
ちょっとググれば、Ctrl-qを使うことで改行やタブが入力できることが分かる。改行はCtrl-q Ctrl-j、タブはCtrl-q Ctrl-iだ。しかし、なぜ改行がCtrl-jでタブがCtrl-iなのかはあまり説明がない。こういうのは理屈が分かっていないと覚えられないし、何より応用が効かない。例えばForm Feed (0x0C) を入力したい場合はどうすれば良いだろう?
"emacs ctrl-q"でググったところ、Google Booksにある"Unix Power Tools"という本の一部が引っかかった。曰く:
Ctrl-q tells Emacs that the next character you types is text, not a part of some command. So the sequence Ctrl-q Ctrl-l inserts the character Ctrl-l into your file;
適当な訳
Ctrl-qは、あなたがタイプしたのがテキストで、コマンドの一部ではないことをEmacsに伝える。そのため、Ctrl-q Ctrl-lというシーケンスは文字Ctrl-lをファイルに入力する。
うーん、これだけだとちょっと分からない。と、ここでWikipediaの「コントロールキー」の項目を見ると、コントロールキーの歴史として以下のようなことが書いてある。
テレタイプ端末、及び初期のコンピュータキーボードでは、コントロールキーを押しながら他のキーを押すと、生成されるASCIIの下位5ビット以外がゼロとなった。これによりユーザーはASCIIの非表示文字である最初の32文字 (0x00 - 0x1f) を生成、入力できる。
これで何となく分かった気がする。小文字のjはASCIIコードで0x6A、2進数なら01101010。下位5ビット以外を0にすると00001010 = 0x0A、つまり改行を表すLFになる。また、小文字のi (0x69 = 01101001) についても同様に処理すれば00001001 = 0x09 (タブ) となる。つまり、入力されたキーのASCIIコードと0x1fとのANDを取ればよい。
ということで、原理が分かればあとはASCIIコード表と簡単な計算でどんな制御コードでも入力できるようになる。と思ったのだが、実際にはCtrl-qの後に入力できる文字は限られているようだ。例えば、Ctrl-q Ctrl-, を打っても"C-, is not a valid character"と言われてしまう。試した結果、Ctrl-q Ctrlの次に以下のキーを押せば0x00-0x1Fを入力することができる。
Ctrl-q Ctrlの後に押すキー | そのキーの16進表記 | 上位3ビットを0 (=入力される制御コード) |
---|---|---|
@ | 0x40 | 0x00 |
a | 0x61 | 0x01 |
b | 0x62 | 0x02 |
c | 0x63 | 0x03 |
d | 0x64 | 0x04 |
e | 0x65 | 0x05 |
f | 0x66 | 0x06 |
g | 0x67 | 0x07 |
h | 0x68 | 0x08 |
i | 0x69 | 0x09 |
j | 0x6A | 0x0A |
k | 0x6B | 0x0B |
l | 0x6C | 0x0C |
m | 0x6D | 0x0D |
n | 0x6E | 0x0E |
o | 0x6F | 0x0F |
p | 0x70 | 0x10 |
q | 0x71 | 0x11 |
r | 0x72 | 0x12 |
s | 0x73 | 0x13 |
t | 0x74 | 0x14 |
u | 0x75 | 0x15 |
v | 0x76 | 0x16 |
w | 0x77 | 0x17 |
x | 0x78 | 0x18 |
y | 0x79 | 0x19 |
z | 0x7A | 0x1A |
[ | 0x5B | 0x1B |
\ | 0x5C | 0x1C |
] | 0x5D | 0x1D |
^ | 0x5E | 0x1E |
_ | 0x5F | 0x1F |