KengoSawa2の技術的ななにか

IT屋さんのようなKengoSawa2がなんかそれっぽい事を書いていくblogです

Anycaオーナーになる際のリスクと対策

内製ツールのコーディングに明け暮れてすっかり記事を書かなくなったKengoSawa2です。

今日は唐突ですが、Anycaを利用する際のオーナー、ユーザーそれぞれのメリット、デメリットと
オーナーになる際に軽視されがちなリスクの対策についてまとめたいと思います。

Anycaってなに?

AnycaはDeNAさんが提供する、個人間カーシェアサービスです。
「自家用車のオーナーと、その自家用車を利用したい個人とを仲介する」サービスです。
詳細は以下公式ページを参照のこと。
anyca.net

私は社会インフラとしてのカーシェアシステムに興味があったことや、ネオヒストリック車愛好家として昔の車の良さを知ってほしい、
小銭稼いで駐車場代やサーキットのタイヤ代にできたらいいな
なんてのがあり、Anycaを利用しています。


Anycaを利用するメリットとデメリットとして、はオーナー側、利用者側それぞれに以下のような点が挙げられます。


オーナー側のメリット:

  • 仲介手数料が安い(オーナーが設定した利用金額の10%)
  • サービス月額費が無料
  • レビューシステムがきちんとしているので、あまりにもアレゲな人はお断りできる。
  • オーナー側で価格設定を行えるので、貴重な車であれば利益が見込める。

オーナー側のデメリット:

  • なんだかんだでやり取りが面倒な時も(特にお断りする場合)前日に機械洗車したり。
  • どこにでもあるような車の場合、価格競争に陥りやすいので手間のわりに大して儲からない。
  • 自車を壊されるリスク(対策は後述)

利用者側のメリット:

  • ありふれた車を借りる場合なら、かなり安い(タイムズプラス等のカーシェアサービスと比べて半額の場合もある)
  • 珍しい車、一度は運転してみたかった車を運転できる

利用者側のデメリット:

  • なんだかんだでやり取りが面倒(断わられる可能性、文章考えないと、対面面倒くさい、傷の有無のトラブルetc...)
  • 格安で借りられるのはよかったが、都度1500円の一日保険に強制加入なので、微妙にお得感が薄い。

簡単にメリット、デメリットを並べましたが、個人的にはよくできているサービスだと思います。
とはいえ、Anycaオーナーには様々なリスクがあります。

Anycaオーナーのリスク対策

Anycaオーナーのリスク対策ですが、結論から先に言います。

  1. 怪しい人物には質問を投げて、少しでも不安なら遠慮なく断りましょう
  2. 万が一の事故時にこじれないよう、ドライブレコーダを装着しましょう
  3. 修理費用、または時価相当額が300万円を超える自動車は登録しないようにしましょう
  4. 修復歴有り車になってしまった場合に備えて、「共同使用条件」に事故減価額の支払義務を明記しましょう

一つずつ解説していきます。

1.怪しい人物には質問を投げて、少しでも不安なら遠慮なく断りましょう

私の個人的な印象ですが、まだアーリーアダプタな感じの人が多く、まともな人が多いです。
しかし、言い方はなんですがちょっとアレゲな人がシェア希望を出してくることだってもちろんあります。
自分の人生経験をフルに活かして、貸しても大丈夫な人物かどうか、ちょっとした質問をしてみましょう。
ちなみに、私が出した質問は以下のようなものでした。

  • スポーツ車の運転経験
  • 運転歴
  • 使用目的および行き先(プライバシーにかかわらない程度で)

返答の内容が大事なわけではなく、こういう質問に対してどう答えてくるかがポイントです。
まともな文章の書ける人なら、まあまず大丈夫でしょう。
純粋な悪意で来られたらお手上げですが、そんなこと考えてたら一生貸せませんし。そこは諦めましょう。

2.万が一の事故時にこじれないよう、ドライブレコーダを装着しましょう

当たり前と言えば当たり前ですが、付けておきましょう。
DeNAさんの説明会で話題にも上ったのですが、駐停車禁止を食らったのに申告しなかった利用者がいたようです(笑)
警察から身に覚えのない駐禁を食らったあげく、利用者と揉めるのはしんどいですよね。。
ドライブレコーダがあればごねようもないです。
駐車中録画機能があれば完璧ですが、とりあえず録画してますよというだけでもかなりの抑止力があります。

3.修理費用、または時価相当額が300万円を超える自動車は登録しないようにしましょう

Anycaでは利用者は1日1500円の自動車保険に強制加入させられます。この保険の車両保険の上限金額が300万円です。
つまり、万が一全損された場合に300万を超える自動車だった場合はオーナー側の丸損となります。
後述の「共同使用条件」で工夫して対策とするか、時価300万以上の価値のある車は提供しないのが賢明だと思います。

2016/08/04 加筆
この車両保険上限金額300万円と自動車保険1500円の仕組みについては、DeNAさん側でも問題として認識しているようです。
改善されたらこの部分に関しては修正加筆したいと思います。

4.修復歴有り車になってしまった場合に備えて、「共同使用条件」に事故減価額の支払義務を明記しましょう

3.の補足のような話になりますが、300万以下の車の場合、万が一事故があった場合でも車両保険で車は直ります。
しかし、一般的に「修復歴あり」とされた場合、車両の価値自体は大きく下がってしまいます。
当然ですが、自動車保険ではその価値低下分は補償してくれません。自己対策した方がよいと思います。

Anycaではオーナーと利用者の間で「共同使用条件」を明記する場所があるので、そこに条件を書くとよいでしょう。
私の場合は以下の記述を「共同使用条件」に加えています。

故意または過失に関わらず車両に修復歴を与えるような事故が発生した場合、
車両の価値棄損の責任はドライバーが負うものとし、以下の2点の金額を車の所有者に支払うものとします。

  • 一般社団法人日本自動車査定協会(JAAI)が発行する「中古自動車事故減価額証明」により示された「事故減価額の評価額」
  • 上記評価額に加えて金30万円

上記条件にした理由ですが、
「修復歴発生による車両の減価額をオーナー個人が判断するのは利用者にとってフェアではない」

と、考えたからです。(自分が利用者なら逆にそんな条件嫌すぎでしょう・・・。)
追い金の30万は「JAAIの検査料+面倒代+JAAIの減価額証明通りにならないリスク代+手間賃」と考えています。

追い金の金額についてはオーナーの提供している車種がどの程度市場価値があるかや、どの程度利用者にリスクを負わせるかという話になるので、
自分の納得できる金額を設定すればいいと思います。

ここまで、細くて嫌な話ばかり続けてきましたが、私はAnycaのサービス設計自体は素晴らしいと思います。
私自身もネオヒストリック車や旧車に乗りたいので、利用者として利用していこうと思っているのです。

それだけに、DeNAさんには上記のようなリスクについて

  • 説明会で補足する
  • Webでの注意喚起
  • 共同使用条件を開く際に事例へのリンクを出して注意を促す

などを進めて貰えたらいいんじゃないかなあと思います。


最後になりますが、私のRX-7です。みんな乗りに来てくださいね!(広告)
2016/08/04時点では諸事情のため貸し出し休止中です
anyca.net

Mac App Storeで販売可能なアプリをビルドするqmakeの例と簡易解説

こんにちは、KengoSawa2です。
レスパスビジョン株式会社で社内向けツールをQtで作成したり、www.lespace.co.jp
開発を行っていたりする謎のエンジニアです。

今日は唐突ですが、上記RapidCopyのqmakeについて、解説適当に内容を晒してみようと思います。

何故qmakeを適当に晒すか?

何故唐突にqmakeを晒すのか?って話になるのですが、理由は単純です。

「Qtを使ってMac AppStore用アプリを作るためのqmakeの参考資料」がゼロだったから

です。
あまりにも情報が無さ過ぎて非常に苦労したので、後続の方がこの記事を見て参考にして楽してくれればなと思い、書いてみました。

早速のqmake

あれこれ解説するよりも、qmakeをそのまま貼った方が良いでしょうということでとりあえず貼りましょう。
例によってですが、一つ一つのコマンドが何をしてるかをなんとなく書いているので、中身の詳細は割愛します。

QT += gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets multimedia

#CONFIGパラメータ指定の意味は以下の通りね
#何も指定しない(コメントアウト) = Mac AppStore用コンパイル(Sandbox対応版)
#pro                       = Pro版をビルド(レスパス直販版かつ、sandbox非対応)
#trial                     = 体験版をビルド(ビルド時刻から2ヶ月経過すると起動ブロック)
#amazon                    = amazon.com販売版をビルド
#上記を組み合わせて使用する

#以下の場合はRapidCopyProを体験版としてビルド指定
CONFIG += pro trial

if(pro){
	#pro版の場合は.appの名称をRapidCopyProとする
	TARGET = RapidCopyPro
	#amazon.com向けの場合はifdefに_AMAZONを指定
	if(amazon){
		DEFINES += _AMAZON
	}
}
else{
	#pro指定がないのでAppStore向け
	#.appの名称をRapidCopyとする
	TARGET = RapidCopy
	#AppStore向けの場合はifdefに_SANDBOXを指定
	DEFINES += _SANDBOX
}

if(trial){
	#体験版の場合はifdefに_TRIALを指定
	DEFINES += _TRIAL
}

#OS Xの開発環境ではQtがコンパイルされた時の環境より新しいバージョンのXCodeがリリースされると
#Appkitのヘッダなどのインクルード先が変更されるため、コンパイル不能になる。
#この時、以下のように最新のインクルード先を指定する必要がある。
#RapidCopyはQt5.4.1を使用するため、10.10までは以下を切る必要なかったのよ。。
QMAKE_MAC_SDK = macosx10.11

#ライブラリ開発じゃなくて、単体アプリケーション作るのでapp指定
TEMPLATE = app

#RapidCopyとRapidCopyProとでアイコンを変える
if(pro){
	ICON = RapidCopyPro.icns
}
else{
	ICON = RapidCopy.icns
}

#日本語翻訳ファイルを指定
TRANSLATIONS += RapidCopy_ja_JP.ts

#-LオプションにOS Xアプリのライブラリをリンクする指定、アプリならまず必須。
LIBS += -framework AppKit

#各種ソース(C,C++)
SOURCES += main.cpp \
	mainwindow.cpp \
	cfg.cpp \
	fastcopy.cpp \
	osl.cpp \
	regexp.cpp \
	tapi32ex.cpp \
	tapi32u8.cpp \
	tapi32v.cpp \
	tlist.cpp \
	tmisc.cpp \
	utility.cpp \
	version.cpp \
	mainsettingdialog.cpp \
	confirmdialog.cpp \
	aboutdialog.cpp \
	xxhash.c \
	finactdialog.cpp \
	smtp.cpp \
	jobdialog.cpp \
	registerdialog.cpp \
	qblowfish.cpp \
	joblistrenamedialog.cpp

#各種ヘッダ(C,C++)
HEADERS += \
	cfg.h \
	fastcopy.h \
	osl.h \
	regexp.h \
	tapi32ex.h \
	tapi32u8.h \
	tapi32v.h \
	tlib.h \
	tmisc.h \
	utility.h \
	version.h \
	resource.h \
	miscdlg.h \
	mainwindow.h \
	mainsettingdialog.h \
	confirmdialog.h \
	aboutdialog.h \
	xxhash.h \
	finactdialog.h \
	smtp.h \
	jobdialog.h \
	cocoaapi.h \
	registerdialog.h \
	qblowfish.h \
	qblowfish_p.h \
	joblistrenamedialog.h

#Objective-cソース
OBJECTIVE_SOURCES += \
	GetCocoaOpenFile.mm

#Objective-cヘッダ
OBJECTIVE_HEADERS += cocoaapi.h

#各種.uiファイル
FORMS	+= mainwindow.ui \
	mainsettingdialog.ui \
	confirmdialog.ui \
	aboutdialog.ui \
	finactdialog.ui \
	jobdialog.ui \
	registerdialog.ui \
	joblistrenamedialog.ui

#Qtの各種リソースファイル
RESOURCES += res.qrc


macx {
	#Mac DevelopperプログラムでAppleに発行して貰うアプリケーション開発元証明書情報文字列
	APPCERT = "3rd Party Mac Developer Application: L'ESPACE VISION CO.,LTD"

	#Mac DevelopperプログラムでAppleに発行して貰うインストーラ開発元証明書情報文字列
	INSTALLERCERT = "3rd Party Mac Developer Installer: L'ESPACE VISION CO.,LTD"
	
	#Mac Developper Programで申請、認可されたBundleIdentifierを指定
	if(pro){
		BUNDLEID = com.LespaceVision.RapidCopyPro
	}
	else{
		BUNDLEID = com.LespaceVision.RapidCopy
	}

	#Qt実行環境へのパス
	QTPATH = ~/Qt5.4/5.4/clang_64

	#ワークディレクトリ設定
	QTPROJECTS = /Users/lespace/QtProjects
	
	#アプリケーションのplistやアプリケーションに同時に格納するQtライブラリのplistを格納するフォルダのパスを設定
	#同ディレクトリ内にアプリに必要なInfo.plistを事前に配置する
	PLISTLOC = $${QTPROJECTS}/plist
	
	#RapidCopyPro専用。インストーラを作成するのに必要な各種リソースへのパスを定義する
	#RapidCopyPro.appからInfo.plistを生成、利用するためのワークディレクトリ
	PROPKGLOC = $${QTPROJECTS}/pkgpath
	#インストーラの各種設定を記述したxmlファイルを格納しているフォルダへのパス。事前に配置しておく。
	PRORESLOC = $${QTPROJECTS}/resource
	
	#pkgbuildコマンドに渡すバージョン番号、次verのリリース時は手で変更する
	PROVER = 1.1.0

	#Mac AppStoreに提出する場合の、SandBox制約例外を記述するplist,エンタイトルメントplistの指定。事前に配置しておく。
	ENTITLEMENTS = $${PLISTLOC}/Entitlementsplist/Entitlements.plist

	#以下,make codesignまたはmake productを実行した時行う作業に関する記述
	QMAKE_EXTRA_TARGETS += codesign product
	#以下の文法を使用してcommandsに+=してから最後にQMAKE_EXTRA_TARGETSにcodesignを追加すると、make codesignでcodesignに追加したcommandsを
	#順番に実行してくれる。
	
	#RapidCopy(Pro).app内にQt実行用ライブラリをmacdeployqtコマンド
	codesign.commands += $${QTPATH}/bin/macdeployqt $${TARGET}.app -verbose=3;

	#事前に記述、用意しておいたRapidCopy(pro)のInfo.plistを最終バイナリ内のInfo.plistに上書きする
	if(pro){
		codesign.commands += cp $${PLISTLOC}/Infoplist_pro/Info.plist $${TARGET}.app/Contents/Info.plist;
	}
	else{
		codesign.commands += cp $${PLISTLOC}/Infoplist/Info.plist $${TARGET}.app/Contents/Info.plist;
	}

	#macdeployqtでコピーした最終バイナリ中にあるQtライブラリのフレームワーク一つ一つをMac Developper Programで受け取った証明書でサインする。
	#(これをやらないとMac AppStoreに提出できない)
	codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/Frameworks/QtCore.framework;
	codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/Frameworks/QtMultimedia.framework;
	codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/Frameworks/QtNetwork.framework;
	codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/Frameworks/QtPrintSupport.framework;
	codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/Frameworks/QtGui.framework;
	codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/Frameworks/QtMultimediaWidgets.framework;
	codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/Frameworks/QtOpenGL.framework;
	codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/Frameworks/QtWidgets.framework;

	#フレームワークへのサインと同様、プラグインのライブラリにも全てサインする
	if(pro){
		codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/PlugIns/audio/*.dylib;
		codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/PlugIns/bearer/*.dylib;
		codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/PlugIns/imageformats/*.dylib;
		codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/PlugIns/mediaservice/*.dylib;
		codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/PlugIns/platforms/*.dylib;
		codesign.commands += codesign -f -s \"$${APPCERT}\" $${TARGET}.app/Contents/PlugIns/printsupport/*.dylib;
	}
	else{
		codesign.commands += codesign -f -s \"$${APPCERT}\" -i $${BUNDLEID} $${TARGET}.app/Contents/PlugIns/audio/*.dylib;
		codesign.commands += codesign -f -s \"$${APPCERT}\" -i $${BUNDLEID} $${TARGET}.app/Contents/PlugIns/bearer/*.dylib;
		codesign.commands += codesign -f -s \"$${APPCERT}\" -i $${BUNDLEID} $${TARGET}.app/Contents/PlugIns/imageformats/*.dylib;
		codesign.commands += codesign -f -s \"$${APPCERT}\" -i $${BUNDLEID} $${TARGET}.app/Contents/PlugIns/mediaservice/*.dylib;
		codesign.commands += codesign -f -s \"$${APPCERT}\" -i $${BUNDLEID} $${TARGET}.app/Contents/PlugIns/platforms/*.dylib;
		codesign.commands += codesign -f -s \"$${APPCERT}\" -i $${BUNDLEID} $${TARGET}.app/Contents/PlugIns/printsupport/*.dylib;
	}

	#バンドル内のフレームワーク及びプラグイン全てにコードサインが終わったらバンドルである.appをコードサインする
	if(pro){
		codesign.commands += codesign -f -s \"$${APPCERT}\" -v $${TARGET}.app;
	}
	else{
		#Mac AppStoreに提出する場合にはここでEntitlementsのplistを指定しておく必要がある。
		codesign.commands += codesign -f -s \"$${APPCERT}\" -v --entitlements $${ENTITLEMENTS} $${TARGET}.app;
	}

	#ここからproductbuildコマンドを使用した最終ビルドの組み立て処理
	if(pro){
		#pro版の場合は、ようこそ的なインストーラ画面の設定や旧verが既に存在していた場合の対応などを手動でルール決めするのでやや複雑。

		#codesignまで終わったバンドルを一時pkgディレクトリに複製
		product.commands += cp -rf $${TARGET}.app $${PROPKGLOC};
		#一時pkgディレクトリ内のバンドルからproductbuildに必要なplistを逆生成する
		product.commands += pkgbuild --analyze --root $${PROPKGLOC} $${PROPKGLOC}/$${TARGET}.plist;
		#逆生成したplist内にplutilコマンドを使って、上書きインストール時の挙動設定を行う。
		#BundleIsRelocatableをfalseに設定しないと、既に古いverがインストール済みの場合に正しくインストールしてくれない
		product.commands += plutil -replace BundleIsRelocatable -bool false $${PROPKGLOC}/$${TARGET}.plist;
		#BundleIsVersionCheckedをfalseに設定しないと、あえて古いverを上書きインストールしたい場合に戻れなくなる
		product.commands += plutil -replace BundleIsVersionChecked -bool false $${PROPKGLOC}/$${TARGET}.plist;
		#いじくったplistを元に、インストール用のサブコンポーネントを作成する。
		#補足:RapidCopy(Pro)ではサブコンポーネントが1個しかないので、下のコマンドが一個だけど複数のパッケージを一つのインストーラに合体させたりもできるらしい。
		product.commands += pkgbuild --root $${PROPKGLOC} --component-plist $${PROPKGLOC}/$${TARGET}.plist --identifier $${BUNDLEID} --version $${PROVER} --install-location /Applications $${TARGET}.pkg;
		#最終インストーラをproductbuildコマンドを用いて作成する。
		product.commands += productbuild --distribution $${PRORESLOC}/Dist.xml --package-path $${PROPKGLOC} --resources $${PRORESLOC} $${TARGET}Installer.pkg;
		#一時pkgディレクトリ内の各種一時ファイルを削除
		product.commands += rm -rf $${PROPKGLOC}/*;
	}
	else{
		#AppStore版の場合はインストーラ部分が不要、かつバージョン管理などは全てAppStoreの仕掛けにお任せなので、以下だけでok
		product.commands += productbuild --component $${TARGET}.app /Applications --sign \"$${INSTALLERCERT}\" $${TARGET}.pkg;
	}
}

実際に製品ビルドするときは

僕の場合は以下のような適当なシェルスクリプトを用意して、製品ビルドするときだけシェルをターミナルから叩いています。

!/bin/bash

cd /Users/lespace/QtProjects/build-rapidcopy_main-Desktop_Qt_5_4_1_clang_64bit-Release
rm -rf RapidCopy*.app
make clean
make
make codesign
make product

内容はなんというか、だいぶ適当ですがポイントは

make codesign
make product

のところですね。
この引数はQMAKE_EXTRA_TARGETSで指定した任意文字列なわけです。

QtCreatorにはmakeに引数を渡して実行する機能もあるので、それを活用するともっと便利かもしれませんが、
ターミナルにばらばらーっと出てくれる方が好みなので、とりあえずはこれでいいやー感です。
大規模開発の方などはもっと色々詰めないといけないでしょうね。

今回の記事を書くにあたって、無駄なところを色々消したりして綺麗にしてみましたが、
もっと頭の良いやり方はいっぱいあると思います。。
OSXのqmakeに詳しい方のツッコミお待ちしております!!

また、Mac環境でQtを使って開発をしているかた、twitterKengo Sawatsu (@KengoSawa2) | Twitter
でも、Qt勉強会でもなんでも良いので、是非情報交換しましょうー。

明日(12/2)はQt勉強会のおやつ部部長緑の翁 (@hermit4) | Twitterさんの記事です!!

xxhashの紹介

プロダクションEXPO2015でRapidCopyの展示員をしているのですが、当然暇なので
xxHashの紹介をしたいと思います。

xxhashってなに?

xxhashはyann Collet氏が開発したファイルチェックサム専用の軽量ハッシュライブラリ(正確に言うとアルゴリズム)です。
実装はc++用が作者自身によって公開されていて、BSDライセンスなおかげもあって様々な言語で実装されてます。

github.com

今どきの主要言語はほぼ全てサポートされているので、是非コピペ実装してみてください。
で、このxxhash何が優れているか?って話になるのですが。まとめると以下のようになります。

例えばみなさんおなじみのMD5なんかと比べると、ハッシュ生成速度はおよそ15倍。
CPU使用率は僕の開発マシンのSandyBridgeベースのCore i7 2Ghz環境下だとMD5比でほぼ半分です。
これは非常に優秀な結果だと思います。
その他のアルゴリズムとの比較結果なんかもyann氏のページに乗っていますので、是非見てください。

僕が開発しているRapidCopy
[RapidCopy] MacOSX用高速ベリファイ差分ファイルコピーソフト

なんかでは、MacBookAir等の非力なモバイルCPU環境下でチェックサム生成する時に効果が確認できます。
最近のSSDRAIDデバイスは非常に高速なので、案外CPUリソースを食ってたりするんですよね。。
僕の場合はしょせんたかが1コピーワークロードですが、データセンター屋さんなどでは、巨大なコピーを延々やっていると想像されるわけで。
xxHashでチェックサムを行えば、センター全体の消費電力低減などにも効果があるんだろうなーなどと妄想しています。


話変わって、xxhashには当然欠点もあります。

  • 暗号化、復号化のための機構を省くことで速度を稼いでるのでセキュリティ目的での使用はできない
  • ハッシュキー長が64bitなので、ファイルの数が膨大だった場合に2つの異なるデータにおいて同一のハッシュ値を表示してしまう確率がちょっときになる。

一番目の話題は特に大事で、勘違いしてはいけない大事な部分です。

二番目のハッシュ衝突耐性ですが、基本的には気にしないで大丈夫です。
滅多に衝突しません。
コピー元とコピー先の異なるデータで同一のハッシュ値をたまたま算出する確率は天文学的に低いはずです。はず。

ただ、RapidCopy(正確に言うとRapidCopyの移植元であるFastCopy)では数百万ファイルなどのヘビーなファイルコピーを想定しています。こうなってくると、それなりに気になる確率にもなってきます。まず大丈夫なはずですが。。
上記の点を原作者でもある白水氏からのアドバイスを受け、確かにそうかもと感じたので
僕は128bit化の要望をyann氏に出してみました。

yann氏自身も128bit化の意味はあるとは思っているようで
「時間ないけどその内やりたいんだよね。今年のうちにやれたらいいなー」的な前向きなお返事を貰いました。
気長に待つが吉か、128bit版のコントリビュートがあれば素敵と思います。
(僕は数学ダメダメな人なので貢献できません、ごめんなさい。。)
xxhash,様々な局面で活用する価値があると思います。
みなさんも是非使ってみてください。


この記事はyann Collet氏twitter.com
やRapidCopyの移植元でもあるFastCopyの原作者 白水啓章氏twitter.com

からのアドバイスを受けて記述しました。
両氏の深い技術力と的確なご意見に感謝します(._.)

2015/7/2
白水氏より


とのご指摘を受けました。
ご指摘ありがとうございます&見解の誤りでご迷惑おかけして申し訳ありません(._.)
衝突の説明について、2つの異なるデータで同一のハッシュを生成する確率の問題であることを明記すると同時に、当該確率表の説明を削除しました。

2015/7/9
yann氏への連絡に関する下りに関して、yann氏に連絡しようと思ったくだりを追加。
白水氏からに確率の教示を受けなければ128bit要望はたぶん出さなかったと思います。。

Qtの暗号化ライブラリについて

[RapidCopy] MacOSX用高速ベリファイ差分ファイルコピーソフト
RapidCopyの直接販売版の開発で、ちょっとした暗号化を施す必要があったので、調べてみました。

Qtにはハッシュ値を算出するための
QCryptographicHash Class | Qt Core 5.4
が、あります。

しかし、これはハッシュ値を算出するだけなので、暗号<->復号には使えません。

で、Qt上で簡単に暗号化<->復号化するライブラリないのかなあ。
と探してみたわけですが、結論から先に言うとまずはQCAをお試しあれです。
リンクは以下です。
Delta XMPP Project

f:id:seattlei:20150628154812p:plain

QCAは単純な暗号化<->復号化だけじゃなく、RSAみたいな公開鍵関係も扱える
LGPLライセンスの総合暗号化ライブラリです。

詳細はリンク先参照ですが、公式ホームページのバイナリが既にやや古くなっているので、
KDE QuickGit :: qca.git/summary
gitからソースを持ってきてコンパイルする方が良いと思います。
僕の目的でもあるBlowfish - Wikipedia暗号にも対応しています。
(#pkcs5パディングには非対応なので、元データは8バイトの倍数にする必要があります)

しかし、僕の環境(OS X 10.10.3)のQt Creatorのデバッグビルド動作では問題ないんですが、
リリースビルドにするとqcaのdeallocateでクラッシュするという悲しい現象が発生(´・ω・`)
色々しらべてみもしたんですが、諦めちゃいました。
OS XでQCA普通に使えてるよーって方、教えてください。。



で。気を取り直して調べなおしたところ、github
https://github.com/roop/qblowfish
f:id:seattlei:20150628161405p:plain

なるシンプルなクラスを公開している人を発見。
結局これを使いました。
8バイト倍数じゃないデータでも自動でパディングする「#PKCS5」に対応してるのがグッドです。

使い方は至ってシンプルですので、githubのサンプルを見てみてください。
気が向けば、適当なサンプルを書こうかなあと思います。

2015年3月~4月のitunesConnectServer仕様変更がデグレードしてるかもな件

f:id:seattlei:20150415153431p:plain

現在進行形で、まだ白黒ついていないので速報な感じです。
結論が出たらこの記事も更新します。

OS X 10.10.3の公開やXcode6.3、iOS8.3ナドの公開に伴ってiTunesConnect側で以下の問題が発生
している気配があります。

現象:iTunesConnectを通じてアプリケーションビルドを提出しようとすると
ITMS-90451:. "CFBundleIdentifier Collision......」が表示されて受け付けて貰えなくなる。

原因:iTunesConnectServer側の変更?あるいは変更によるデグレード

対策:ありません。Frameworksを同梱するタイプのアプリケーションの場合、当該のFrameworksを同梱しないようにして提出すれば受け取って貰えますが、当然そんなビルドがまともに動くわけはないので。。

海外でも同じような問題で困っている人が多数いるようです。
参考:
xcode - error itms-90451 "CFBundleIdentifier Collision Error" - Stack Overflow

ちなみにこっちが僕の元記事です。(英語がひどすぎるのは気にしないでください)
osx - ITMS-90451:CFBundleIdentifier Collision Error - Stack Overflow

AppleBugReportを通じてAppleに「デグってね?」という意見は出していますので、
その内なにか返事があることを期待したいですが。。

4/29
解決しました!答えは
osx - ITMS-90451:CFBundleIdentifier Collision Error - Stack Overflow
を参考に、だけでは味気なさすぎるので、簡単に日訳します。

大きな問題は2つありました。

1個目はお題になっているitms-90451です。
これは今までMac App Storeに提出する時に発生してなかったエラーが出るようになった、というものです。
原因はFramework内のInfo.plist内に記述するCFBundleIdentifierの内容がAppのInfo.plistと同じ内容を書いていたからでした、まんまですね。
Appleのサポート曰く「これは仕様変更じゃないよ、元々ダメな書き方だったんだけど、最近チェックするようにしたんだよ」とのことです。うーん。。
私が何故同じCFBundleIdentifierを書くようにしたのかはちょっと記憶にない(たぶんネットのどっかのサンプルを参考にした)ので、私のミスと言えばミス・・・ですね。

2個目は実はお題になっていない、itms-90334です。
エラー内容はこんな感じです。
ERROR ITMS-90334: "Bundle identifier mismatch. The executable at QtCore in RapidCopy.app/Contents/Frameworks/QtCore.framework has been signed with identifier 'com.LespaceVison.RapidCopy' which does not match the bundle identifier 'org.qt-project.QtCore'

itms-90451をクリアしても、今度はなぜかこれで怒られるので、以前はここで詰んで諦めていました。
(エラーメッセージを見てもどう対処すればいいのかピンとこなかった)
つい先日、appleのsupportから「これは仕様だよ」と言われて、問題の区分けがはっきりしたところで
気付いたのです。
「これってもしかしてcodesign周りの問題か、。」
で、改めてその視点でググってみたらITMS-90334の対処として「不要なcodesignをする必要はない」との話題が出ており、
これだー!となったわけです。
以下が修正前で
codesign -f -s "YOUR APP CERT" -i "YOUR APP BUNDLEID" "YOURAPP".app/Contents/Frameworks/QtCore.framework

以下が修正後です。-iでappのIDくっつけをやめるだけですね。なんて初歩的な。。
codesign -f -s "YOUR APP CERT" "YOURAPP".app/Contents/Frameworks/QtCore.framework

ITMS-90334に対してメクラになっていたのは、Qtの公式やそれらを修正した各種サンプルを盲信した所にあったようです。実際つい直近まではそれで提出できてたという事実を過信したのが失敗の元でした。。
で、困ったことに3月末以降は、Qtのmake周りのサンプルは軒並み間違っている(同じ問題にはまる)ことになるんですが、。僕の記事に辿り着いてくれるかなあ。。

Qt5.2で登場したQCommandLineParserの使い方について

こんにちは、Qt使い始めて約半年のKengoSawa2です。
クラスの使い方レベルの記事で恐縮なのですが、Qt5.2で登場した「QCommandLineParser」について、簡単に紹介したいと思います。

自己紹介

とある映像編集業の会社に所属している謎のSEです。
現在はWindows用のファイルコピーアプリ「FastCopy」の移植をやっています。

"RapidCopy" MacOSX用高速ファイルコピーソフト

移植の過程でGUIコーディングを楽にするためにQtを使うことを決め、
その御縁からQtユーザ会に参加させて頂いてます。

今回紹介する「QCommandLinerParser」もRapidCopyのCLI起動時の解析のために使用しました。

実行環境

今回の実行環境はMac OS X 10.8.5 + Qt5.3.1で説明しています。
他プラットフォーム(特にwindows)では出力例に関しては結果が異なる可能性がありますので、
ご注意ください。

QCommandLineParserの概要

CommandLineParserはQt5.2から登場したコマンドラインにおける引数チェック、オプションチェック目的に特化したクラスです。

https://qt-project.org/doc/qt-5-snapshot/qcommandlineparser.html

C言語でのコマンドライン解析は地味ですが、意外と労力が要ります。
#更に言うとくだらないバグも作りこみやすい:)
それらを簡単に扱うためのクラスです。

QCommandLineParserの使い方の流れ

QCommandLineParserには使用するためのお作法、おおまかな流れがあります。
先に以下の流れをざっくり理解しておくと良いでしょう。

  1. QCommandLineParserクラスのインスタンスを作成し、アプリケーションの静的な情報を設定する
  2. オプションの情報設定
  3. コマンドライン解析の実行
  4. 解析結果の判定
  5. 2.で設定したオプション情報を1つずつ取り出す

なにはともあれまずコード

流れがなんとなくわかった所で、まずはサンプルコードを眺めてみましょう。
下記のコードはRapidCopyの解析処理の一部を簡略化して説明用に単純にしたものです。

bool MainWindow::CommandLineExecV(int argc, QStringList argv)
{
	int argerr = 0;         //コマンドエラーとして終了する際のリターン値

	//コマンドオプション
	QString CMD_STR       = "cmd";
	QString BUFSIZE_STR   = "bufsize";
	QString ESTIMATE_STR  = "estimate";
	QString VERIFY_STR    = "verify";
	QString SPEED_STR     = "speed";

	QCommandLineParser parser;

	//--help,-hをQCommandLineParserで自動で処理するように依頼
	parser.addHelpOption();

	//オプション以外の純粋な引数についての指定と、解説欄の設定
	parser.addPositionalArgument("path1 path2 ...","Source path to copy.");

	//--cmdオプションの情報設定
	QCommandLineOption cmdOpt(QStringList() << CMD_STR,                //オプション文字列
	                          "Specify operation mode.(Default: dif)", //help表示の際のオプション説明
	                          "noe|dif|upd|for|syn|ver|mov|del",       //オプション指定例
	                          "dif");                                  //デフォルトの指定

	//--bufsizeオプションの情報設定
	QCommandLineOption bufOpt(QStringList() << BUFSIZE_STR,
	                          "Specify the size(MB) of the main buffer for Read/Write opration.",
	                          "num",
	                          "1");
	//--estimateオプションの情報設定
	//引数が不要なオプションを設定したい場合には第三引数と第四引数を省略します。
	QCommandLineOption isestimateOpt(QStringList() << ESTIMATE_STR,
	                                "Estimate complete time.");

	//--verifyオプションの情報設定
	QCommandLineOption isverifyOpt(QStringList() << VERIFY_STR,
	                              "Verify written files data.",
	                              "md5|sh1|xxh|s22|s25|s32|s35|fal");
	//--speedオプションの情報設定
	QCommandLineOption speedOpt(QStringList() << SPEED_STR,
	                            "Specify speed control level.",
	                            "full|autoslow|1-9|suspend");

	//設定したオプション情報をparserに追加
	parser.addOption(cmdOpt);
	parser.addOption(bufOpt);
	parser.addOption(isestimateOpt);
	parser.addOption(isverifyOpt);
	parser.addOption(speedOpt);

	//コマンドラインの事前解析
	//実際のパース処理を行うprocess()では情報設定済以外のオプションが
	//存在すると内部でexit()をコールしてしまうため、事前にparse()してチェックしています。

	//parseがfalse=不明なオプションが設定されている
	if(!parser.parse(argv)){
		//知らないオプションが指定されていたので、対象のエラーオプションを取り出して表示。
		QString error_str("illegal option --");
		for(int i=0;i<parser.unknownOptionNames().count();i++){
			error_str.append("\"" + parser.unknownOptionNames().at(i) + "\"");
		}
		error_str.append("\n");
		QTextStream(stderr) << error_str;
		argerr = -1;
		goto end;
	}
	//コマンドライン解析の実行
	parser.process(argv);

	//解析後の各オプションの設定有無を判定
	if(parser.isSet(cmdOpt)){
		//cmdOptの内容を取得
		QString cmdopt = parser.value(cmdOpt);
		//cmdoptの内容チェック、必要な処理の実装をここに
	}

	if(parser.isSet(bufOpt)){
		//以下略
	}
	if(parser.isSet(isestimateOpt)){
		//以下略
	}
	if(parser.isSet(isverifyOpt)){
		//以下略
	}
	if(parser.isSet(speedOpt)){
		//以下略
	}

end:
	//ここまでのコマンド解析でエラーが一つでもあれば終了
	if(argerr == -1){
		//helpを表示してexit()する
		parser.showHelp(argerr);
	}
	return(true);
}

説明はコメント欄に入れちゃいましたので割愛します:)

この例ではCommandLineExecVという関数になっており、いわゆる「argcとargv」は上位から貰う構造になってますが、
Qt環境下ではQCoreApplication::arguments()を呼び出せばargv相当のものを簡単に取得できます。
argcに関してはQCoreApplication::arguments().count()で一発です。


ヘルプ及びエラーの実行例

MacOSX付属のターミナルでヘルプを実行してみた結果は以下のようになります。
f:id:seattlei:20141216131331p:plain

オプション設定していない文字列を適当に指定すると、以下のように不正引数を出力してからヘルプを表示して終了します。
f:id:seattlei:20141216132008p:plain

QCommandLineParserさんのヘルプ表示で困るところ

さて、なんとなく使い方がわかってきたQCommandLineParserですが、ちょっと困る点もあります。
それは

「ターミナル表示の一行80文字あたりを前提として折り返し位置が固定されている」ことです。

先ほどのヘルプ画像を見てみましょう。

f:id:seattlei:20141216132834p:plain

オレンジ色の部分がオプションの説明文字列なわけですが、なにやら窮屈です。
長い説明を入れてしまうと、行数が嵩んで見栄えが悪くなります。
これは緑色の部分の文字数が多いと、説明欄を圧迫するせいです。

試しに長いオプション指定例の文字数を削ってみましょう。
すると、。
f:id:seattlei:20141216133245p:plain

このように、説明欄が割と広くなります。
私が試した限り、QCommandLineParserではこのバランスの調整は出来ないようなので、
自分なりに妥協出来るところまで文字数を微調整して詰めるしかないようです。

その他あんなことこんなこと

QCommandLineParserにはその他にも

  • -vや--versionに自動で対応するためのaddVersionOption()
  • -lsn 等の指定を -l -s -n としてみなすか否かを設定するsetSingleDashWordOptionMode()
  • 複数の引数がそれぞれ違う意味を持つ場合に説明を追加するためのaddPositionalArgument()

などがあります。色々試してみてください。

まとめ

英語の苦手な私だと公式のexampleがパッと理解できなかったので、今回の記事を書いてみました。
初心者さんのCLI入門として活用して貰えたらいいなあと思っています。
最後になりますが、twitterでボヤいていたら助けて頂いた
QtChampionことTasuku Suzuki (@task_jp) | Twitter氏と
渋川よしき (@shibu_jp) | Twitter氏、ありがとうございました(._.)
はっぴーQtらいふ!!

12/17追記:parser.parse部分の処理に関して無駄に判定多かったので修正しました。QtChampionことTasuku Suzuki (@task_jp) | Twitterさん、ご指摘ありがとうございます!

QSystemSemaphoreが痒いところに手が届かなかった件

環境:Mac OS X + Qt 5.3.2

QSystemSemaphore Class | QtCore 5.3 | Documentation | Qt Project

プロセスの同時起動禁止等に活用できるQSystemSemaphoreクラスですが、
僕の使いたい用途としては、痒いところに手がとどかない悲しいクラスということがわかったので、
なんとなくポイントをメモしておきます。

まず、ざっくりと使い方です。

MyMutex = new QSystemSemaphore(任意のQStringによる排他キー,同時実行可能数,QSystemSemaphore::OpenまたはQSystemSemaphore::Create);

//排他取得。ただし、他のプロセスが先に排他取得していると無限待ちとなる

if(hErrLogMutex->acquire()){
    //排他取得中にやりたいことをここにかく!!

    //あれこれし終わったら排他解放
    hErrLogMutex->release()
}

と、こんな感じで簡単にプロセス間排他を取ることができます。
しかし、acquire()には排他取得を無限に待ってしまう欠点があります。

当然、メインスレッド上でacquire()を出すとイベントループ含め止まってしまうので、くーるくるで応答不能扱いになります(´・ω・`)

で、お馬鹿な僕は以下のように対処しました。

  • QThreadで排他取得専用のスレッドを立てて、そちらで排他取得を実行する



これでメインスレッドは動けるので一安心かと思ってましたが次なる問題が発生。

  • 排他取得要求をキャンセルしたい時、キャンセルする手段がない



QThread側からterminate()したりなんだりと色々試しましたが、ダメでした。
仕方ないのでQSystemSemaphoreの中身であるIPC systemVのシステムコールのsemget,semop,semctlを直接発行して解決しちゃいました。

やりたいことにもよると思いますが、ご参考まで。

なお、QSystemSemaphoreの内部の実装はsemopです。
Qt側でIPC_NOWAITフラグを含む非同期排他取得をサポートしてくれるとありがたいなぁと思いますが・・・・、。