Bill Pugh singleton (initilization-on-demand holder idiom)
概要
Singletonの実装方法を探している際に見つけたエレガントで感動した書き方について紹介する。
一般的なSingletonの実装方法
下記に一般的な実装方法を紹介する。
public class GeneralSingleton { private static GeneralSingleton instance = new GeneralSingleton(); private GeneralSingleton() {} private static GeneralSingleton getInstance() { return instance; } }
もしくは遅延評価させるために下記の例もよく見られる。
public class LazySingleton { private static LazySingleton instance; private LazySingleton() { } private static LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
Bill Pugh Singleton
Bill Pughは考案者の名前である。このSingletonは一言で言うとInitilization-on-demand holder idiomを適用させたSingletonらしい。
public class BillPughSingleton { private BillPughSingleton() {} public static BillPughSingleton getInstance() { return SingletonHolder.INSTANCE; } private static class SingletonHolder { static final BillPughSingleton INSTANCE = new BillPughSingleton(); } }
このパターンの肝はJavaの仕様とInner classであるSingletonHolderにある。
JVMにBill Pugh Singletonがloadされた場合、一旦は初期化は無視される。
そして、SingletonHolderの中のINSTANCEは評価されるまで初期化されないため、呼び出された際に初めて初期化される。
この時、最初に呼び出したスレッドにおいてこの値は確定するため、自然にスレッドセーフになるという特徴を備えている。
ここら辺は正直完全に理解できていないため、後日追記する。
一般的なSingletonとの違い
先に二つのSingletonを紹介したが、下記のような欠点がある。
LazySingletonはスレッドセーフでない。スレッドセーフにするためにはsynchronized修飾子が必要だが、これはパフォーマンス低下を引き起こす場合がある。
Bill Pugh Singletonは上記の欠点をシンプルな書き方で解決が可能である。欠点としてはなぜスレッドセーフになるのか説明しづらい点だと思う。
参考
Java : Path.ofとPaths.ofの違い
概要
プロになるJava 12.1においてPath.ofメソッドが使われているが、Java11以前ではPaths.getメソッドを使用していた。 これらの違いについて調べてみた。
Paths.ofメソッドについて
公式リンク:docs.oracle.com Java 20現在、Paths.ofの内部実装ではPath.ofを呼び出しているだけである。 そのため、どちらを使っても本質的には変わらないと思われる。
導入の背景
Javaにofメソッドが追加された際の一貫性を保つために、Path.ofが追加された。
どちらを使うべきか
本メソッドの本質は与えた文字列をそのままPathに変換することなので、単純な変換を表すofメソッドの方が文脈上わかりやすいと思う。 当然、どちらを使ってもよい
その他
参考のStackOver Flowの参照リンクにおいてJava13においてはPaths.getは将来的に非推奨になる旨が書かれていた。 Java20のドキュメントについて本件を確認してみたが、特にそのような記述はなかった。 とはいえ、レガシーなコードでない限りPath.ofを使っていきたいと思う。
参考
wezterm, exit_behavior="CloseOnCleanExit"の対処法
発端
最近、Rust製ターミナルエミュレータのweztermを利用しているが、
CTRL-Dでターミナルを閉じることができない現象に見舞われた。
この状況が発生すると操作を受け付けなくなり、自分でターミナルを閉じるする他なくなる。
以下、その際に表示される表示エラー文
Process "/usr/bin/zsh" in domain "local" didn't exit celanly Exited with code 127.
This message is shown because exit_behavior="CloseOnCleanExit"
当初はzsh側の問題化と勘違いしていたが、wezterm側の設定であることが判明した。
原因
weztermのexit_behaviorと呼ばれる設定によるもの。下記参照
exit_behavior - Wez's Terminal Emulator
本現象は例えばタイポで存在しないコマンドを実行するなど、
コマンドの終了コードが0以外の際にCTLR-DでEOFを送ると発生する。
解説
exit_behaviorは文字通り、shell終了時のターミナルの制御設定である。
デフォルトの設定値が"CloseOnCleanExit"となっているが、これは下記のような制御を行う。
- 正常終了(0)なら端末を閉じる(Close)
- それ以外なら端末を開いたままにする(Hold)
weztermはCTRL-D実行時に最後のコマンドのステータスコードを読みに行くため、
例えばタイポ時の終了コード:127を参照してHold制御を実行して固まってしまう。
対処法
wezterm.lua等に以下の記述を追加する。本設定により確実にターミナルを終了できる。
exit_behavior = "Close"
なお2022/6/10現在、nightly build限定ではあるが
特定exitコードのみホワイトリスト化する場合はclean_exit_codesを利用可能なようだ。
clean_exit_codes - Wez's Terminal Emulator
Arch Linuxのインストール方法、解説
背景
C++開発用に古いノートPCにubuntuを入れていたが、最近動作が遅くなってきてしまった。
そこで以前より興味のあったArch Linuxを導入した。その際の備忘録を残す。
基本的には英語版の公式wikiを見つつ、各種ブログを見ながらインストールした。
環境
ThinkPad X230
事前準備
インストールメディアの作成
Arch Linuxのイメージを入手し、署名による改竄検証を行う。
インストールメディア作成に当たっては、rufusを用いてUSBに書き込んだ。
UEFIの設定
大半の解説サイトがUEFIモードでのインストールを前提としていたため、
下記のUEFI設定をチェックする。
特に自分の環境はUEFI/LegacyがBIOS優先になっていたのため、当初はUEFIに入れなかった。
USB boot
ここからはUSB起動後の話。
ログ記録
scriptコマンドによって、実行したコマンドが記録される。
オプション無しならカレントディレクトリにtypescriptと呼ばれるログができる。
インストールには直接関係ないが、念のため。
script
キーボードレイアウト
US配列を使用しているので割愛
boot modeチェック
以下のディレクトリがあればUEFI。無ければBIOS。起動画面でも判別可能。
ls /sys/firmware/efi/efivars
ネットワーク接続
無線wifiのため、iwctlコマンドで接続。
念のためping確認もしておく。
余談だがこれはusbには入っているコマンドであり、
インストールされるArch Linuxには入っていない。
別途ネットワーク関連のコマンドのインストールが必要(2敗)。
iwctl [iwd] device list [iwd] station hoge_device scan [iwd] station hoge_device get-networks [iwd] station hoge_device connect hoge_SSID [iwd] exit ping archlinux.org
system clockの更新
timedatectl set-ntp true
パーテーション
良く知らなかったが、linuxはパーテーション分割するのが常識らしい。公式WikiのUEFI with GPTを参考にした。
- boot 起動に必要
- root 各種システムファイル
- swqp メモリ領域の拡張。一応作った
diskの確認
まずdiskを確認する。
fdisk -l
fdisk対話モード開始
上記で確認した対象のdiskを対象に操作を行う。ここからはfdiskの対話モードで設定
fdisk /dev/sda
パーテーションテーブル
パーテーションテーブルはGPTが良さげなのでそれにする。コマンドはg
g
パーテーションの追加
それぞれの容量はこの以下とした。swap領域の目安は8GB RAMの場合は4GB~10GBらしいので最大値にしておいた。
- boot /dev/sda1 512MB
- root /dev/sda2 残り全部
- swap /dev/sda3 10GB
nでパーテーション追加。以下のコマンドは割愛。領域は+512Mや-10GBなど相対指定可能。
n
パーテーションタイプ
tでパーテーションタイプ設定。
その後対象パーテーションとタイプを指定する。
タイプの番号はboot領域はuefi(1)、swap領域はswqp(19)
t
設定書き込み
wで設定を書き込み、対話モード終了。qだと書き込まず終了
w
フォーマット
mkfsコマンドでフォーマットしていく。
wiki曰くEFI system partitionはfat32が推奨らしい。
rootはext4が一般的。
サイトによってはmkfsを利用していたが、
wikiに倣ってmkswapを用いた。
mkfs.fat -F32 /dev/sda1
mkfs.ext4 /dev/sda2
mkswqp /dev/sda3
マウント
これもwikiに倣いEFI system partitionは/mnt/bootに、rootは/mntにマウントする。
/mnt/bootの作成を忘れないこと。swapはswaponコマンドで有効化するらしい。
mount /dev/sda2 /mnt
mkdir /mnt/boot
mount /dev/sda1 /mnt/boot
swapon /dev/sda3
--追記(2021/11/5) /mnt/bootの作成とマウントは/mntのマウント後でないとまずいようだ
ミラーサイトの更新
/etc/pacman.d/mirrorlistにミラーサーバの設定が書いてあり、日本サーバーが優先になるように設定する。
本来ならば、上記のファイルの順番を変えるだけで対応可能である。
が、自分の環境では日本サーバーの設定がなかったため、日本サーバー情報を取得した。
以下は日本サーバにおいて24時間以内にhttpsで接続されたサーバ情報を通信速度順にソートをかけて保存するコマンド。
reflector --country 'Japan' --age 24 --protocol https --sort rate --save /etc/pacman.d/mirrorlist
パッケージのインストール
いよいよパッケージインストール。最低でも以下が必要。
また、このタイミングで以下のパッケージも入れておいた方が良い。
入れ忘れたパッケージがあっても、
後述のchroot後にpacmanコマンドで入れてもよい。
自分の環境では以下とした
pacstrap /mnt base linux linux-firmware vim iw wpa_supplicant dhcpcd
Note
上記以外のパッケージはOSインストール後でも間に合う。
archlinux的考え方で行けば上記すら不要かもしれないが、
以下の理由で先に導入した。
linux力が高ければ不要だと思う。
* テキストeditorがなく、OSインストール後のシステムファイルを変更できなかった。
* ネットワークコマンドの使い方がわからず、パッケージ更新を行えなかった。
fstabの書き出し
linuxのファイルマウントの定義のことらしい。
UUIDと呼ばれる一意に定まるIDを用いてパーテーションをマウントする設定を書き出す。
genfstab -U /mnt >> /mnt/etc/fstab
なお、UUIDは以下のコマンドで確認可能。/mnt/etc/fstabと比較するのも良いだろう。
lsblk -f
ルートディレクトリの変更
ルートディレクトリを変更することによってインストールしたシステムを操作していく、のだと思う。
arch-chroot /mnt
timezoneの設定
ln -sf /usr/share/zoninfo/Asia/Tokyo /etc/localtime hwclock --systohc --utc
localeの設定
/etc/locale.genのen-US.UTF-8 UTF-8とja_JP.UTF-8 UTF-8をアンコメントする。
en_US.UTF-8 UTF-8 ja_JP.UTF-8 UTF-8
次のコマンドを実行し、設定を反映
locale-gen
また、以下の設定ファイルを作成
LANG=en_US.UTF-8
ホストネームの設定
以下で設定。
hogehoge
また、ホスト名とipアドレス設定ファイルを作成する
127.0.0.1 localhost ::1 localhost 127.0.1.1 hogehoge.localdomain hogehoge
initramfsの設定
RAM Filesystem、つまり、メモリ上のファイルシステムのこと。
/etc/mkinitcpio.confをいじって下記コマンドで反映させる。基本的には不要らしいが、一応実行しておいた。
mkinitcpio -P
rootのパスワード設定
passwd
ブートローダー
ブートローダーの設定も必要らしい。
今回はwikiを見て良さげだったのでGRUBを採用した。
UEFI systemの場合、efibootmgrのインストールも必要。
pacman -S grub pacman -S efibootmgr
efi-directoryはEFIをマウントした、/bootを指定
bootloader-idはUEFIから確認できる名前っぽい。
grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=hoge
以下のコマンドでconfig fileを作成する。
-oは出力する設定ファイル名を指定するオプション。
grub-mkconfig -o /boot/grub/grub.cfg
マイクロコード
マイクロコードはプロセッサのファームウェアと同義らしい。 intel製CPUなので、intel-ucodeをインストール
pacman -S intel-ucode
上記のloadタイミングは色々選べるようだが、 GRUBにより起動時のloadを選択した。
grub-mkconfig -o /boot/grub/grub.cfg
終了処理とログの書き出し
ルートから抜け出す。
exit
次のexitでscriptを終了し、ログを終了する。
exit
usb内のデータは削除されるので、これをbootに移す
cp typescript /mnt
再起動する。自動でアンマウントされるので、マウント処理は割愛。
Hello Arch Linux
起動することを確認する。
その他
後日、ArchLinuxインストール後の処理を書く予定
参考
本家wikiが日本語版・英語版共に役に立った。内容は英語>日本語で参考にした。
ブログも大変参考になった。
- Archlinux 本家wiki
archlinux.org - Archlinux 日本wiki
archlinux.jp - Arch Linuxのインストール記事1
Arch Linux インストール俺々式完全版 - Arch Linuxのインストール記事2
ArchLinuxのインストール
テンプレートクラス内のusingによるエイリアス宣言について
背景
以下のようなテンプレートクラスにおいて、
使用する型を短縮したい場合がある。
例えばコンテナ型を用いる場合、テンプレートが入れ子になって
ごちゃごちゃして見づらい。
template <class T, class K> class ScoreTable { public: // 型名が長すぎる using Dmap = std::unordered_map<T, std::unordered_map<K, int>>; int GetScore(T row, K col); void SetScore(T row, K col, int val); void SetDmap(Dmap &dmap); private: std::unordered_map<T, std::unordered_map<K, int>>dmap_; };
これをこうしたい。
// どこかで以下を宣言 using Dmap = std::unordered_map<T, std::unordered_map<K, int>>; // 型がすっきりに template <class T, class K> class ScoreTable { public: int GetScore(T row, K col); void SetScore(T row, K col, int val); void SetDmap(Dmap &dmap); private: Dmap dmap_; };
問題点
テンプレートクラスで使うためにはグローバル空間で
using宣言はできない?と考えclass内でusing宣言にする作戦。
ただ、void SedDmap()関数は引数に Dmap型を使うため、
外部にDmap型を知らせるのかわからなかった。
結論
classのpublicに型エイリアスを宣言する。
エイリアスをクラスから参照できることを知らなかった。
適当にクラス内で宣言して使っていたが、
privateになっていることが頭から抜けていた。
ソースコード
double_map.h
#ifndef DOUBLE_MAP_H_ #define DOUBLE_MAP_H_ #include <unordered_map> template <class T, class K> class ScoreTable { public: using Dmap = std::unordered_map<T, std::unordered_map<K, int>>; int GetScore(T row, K col); void SetScore(T row, K col, int val); void SetDmap(Dmap &dmap); private: Dmap dmap_; }; template <class T, class K> int ScoreTable<T, K>::GetScore(T row, K col) { return dmap_.at(row).at(col); } template <class T, class K> void ScoreTable<T, K>::SetScore(T row, K col, int val) { dmap_.insert({row, {}}); dmap_[row].insert({col, val}); } template <class T, class K> void ScoreTable<T, K>::SetDmap(Dmap &dmap) { dmap_ = std::move(dmap); } #endif
上記クラスを使う側
double_map.cc
#include <iostream> #include <string> #include "double_map.h" int main() { using Stbl = ScoreTable<std::string, std::string>; Stbl dmap; Stbl::Dmap set_map = {{"Tom", {{"english", 95}, {"math", 75}}}, {"Jaws", {{"english", 63}, {"math", 52}}}}; dmap.SetDmap(set_map); std::string student = "Tom"; std::string subject = "math"; std::cout << student << " : " << subject << " : score : "; std::cout << dmap.GetScore(student, subject) << std::endl; return 0; }
出力
Tom : math : score :75
using template type parameter ‘T’ after ‘class’ エラーについて
発端
C++テンプレートの勉強をしていた時の備忘録
試しに二次元連想配列(キーを二つ持つ配列)をテンプレートクラスを
使ったところ、コンパイルエラーが発生した
以下コンパイルエラーの一部
double_map.h:18:21: error: using template type parameter ‘T’ after ‘class’ 18 | int DoubleMap<class T, class K>::GetValue(T row, K col)
原因
メンバ関数の定義の際に、'class' をつけてしまっていた。
こんなミスだったなんて・・・
template <class T, class K> int DoubleMap<class T, class K>::GetValue(T row, K col) { return map_.at(row).at(col); }
上記のint DoubleMap< class T, class K>部分
定義の際にはint DoubleMap< T, k>で良い。
template <class T, class K> int DoubleMap<T, K>::GetValue(T row, K col) { return map_.at(row).at(col); }
結論
エラーはよく読もう
ソースコード
#ifndef DOUBLE_MAP_H_ #define DOUBLE_MAP_H_ #include <unordered_map> template <class T, class K> class DoubleMap { public: int GetValue(T row, K col); void SetValue(T row, K col, int val); private: std::unordered_map<T, std::unordered_map<K, int>> map_; }; template <class T, class K> int DoubleMap<class T, class K>::GetValue(T row, K col) { return map_.at(row).at(col); } template <class T, class K> void DoubleMap<class T, class K>::SetValue(T row, K col, int val) { map_.insert({row, {}}); map_[row].insert({col, val}); } #endif
正しくはこちら
#ifndef DOUBLE_MAP_H_ #define DOUBLE_MAP_H_ #include <unordered_map> template <class T, class K> class DoubleMap { public: int GetValue(T row, K col); void SetValue(T row, K col, int val); private: std::unordered_map<T, std::unordered_map<K, int>> map_; }; template <class T, class K> int DoubleMap<T, K>::GetValue(T row, K col) { return map_.at(row).at(col); } template <class T, class K> void DoubleMap<T, K>::SetValue(T row, K col, int val) { map_.insert({row, {}}); map_[row].insert({col, val}); } #endif
TechMex について
TechMexについて
日々の学んだことを備忘録として残すためにブログ開設しました プログラミング、ロボット技術関係の記録を残していきます