LocalAppData にアプリケーションをインストールするという選択
GoogleChrome は ProgramFiles ではなく、LocalAppData(C:\Users\[ユーザー名]\AppData\ もしくは C:\Documents And Settings\[ユーザー名]\Local Settings\Application Data\)にインストールされる。
このLocalAppDataは(MSがどう考えるかわからないが)システムに影響を与えない個人向けのアプリケーションのデフォルトのインストール先の選択肢として検討すべきものであると思われる。
メリットとしては、
- アクセスに管理者権限(IntegrityLevel High)の必要がない
- 64bit環境のディレクトリリダイレクトの影響を受けない
などがある。
デメリットとしては、ユーザーごとのディレクトリであるため、マシン単位でのインストールが出来ない、共有DLLなどを配置することができない、ひとつのマシンに複数インストール可能で容量的に無駄、などがある。が、これはアプリケーションの特徴によっては特に問題とはならない場合がおおい。
インストール先のディスク容量は、最近のPCでは特に大きなアプリケーションでない限り無視して良い。個人製作のアプリなどではなおさらであろう。
共有DLLは確かに使えないが、最近は共有するがゆえのバージョン管理などが問題になり、サイドバイサイドといった新しい機構が導入されている。これを「共有はしない、DLLは常に自exeからの相対で探す」というポリシーにすれば、バージョンの複雑な問題も未然に回避できることになる。
マシン単位でのインストールはできないが、それは「個人でインストールしたものが他人によってかってに削除、更新されることがない」ということでもあり、一概にデメリットであるということはできない。勝手にアプリケーションをインストールされては困るという管理者側の要求もあるかもしれないが、どちらを取るべきかはアプリケーションの質によるものであろう。
アプリケーションの作りとしては、インストールパスがどこであるかの前提などは持たせず、どこに配置しても相対パスで正しくリソースにアクセス出きるようにし、インストール先はユーザーが決定すべきものである。
自分でインストール先を決定できるユーザーにとっては LocalAppData にアプリケーションがインストールされることは直感に反する挙動であり、それは避けるべきであるのだが、多くのユーザーがインストール先を自分で決定するだけのリテラシーを持たないというのも現実であり、そのようなユーザーにとってはアプリケーションがどこにインストールされようが実際に問題とはならないということも事実である。
アプリケーションの性質、ユーザーの特性によっては、大多数のユーザーが後者であることも十分考えられる。
以上のような理由から、アプリケーションの性質によってはデフォルトのインストール先を LocalAppData 以下にすることは十分検討すべきであると思われる。
UserProfile内の、(Localではない)AppData(C:\Users\[ユーザー名]\AppData\Roaming もしくは C:\Documents and Settings\[ユーザー名]\Application Data)にはアプリケーションは配置してはならない。ローミング環境で問題になる。
typesafe な sprintf
boost::format を使えばいいんだけど、operator%がなんとなく嫌だったり、フォーマット不能な場合にデフォルトで例外投げるのが嫌だったりするので、そのへんを補正してsprintf感覚で使えるようにしたマクロ。引数10個まで行ける。
#include <boost/preprocessor.hpp> #include <boost/format.hpp> #include <boost/algorithm/string.hpp> // arg0 % arg1 % arg2 ・・・と追加していくためのマクロ。 #define X_GENERATE_FORMAT(z,n,x) %arg##n // 関数本体生成マクロ. // テンプレートパラメータ、 // 第2以降の引数、 // フォーマットに % で追加していく部分 // の3箇所がさらにマクロを呼ぶ。 #define X_GENERATE_FORMAT_FUNCTION(z,n,d) \ template< BOOST_PP_ENUM_PARAMS(n,typename T)> \ inline std::string _fmt( const char* format, BOOST_PP_ENUM_BINARY_PARAMS(n,const T,& arg) ) \ { \ boost::format f; \ f.exceptions( boost::io::no_error_bits ); \ f.parse(format); \ f BOOST_PP_REPEAT(n,X_GENERATE_FORMAT,_); \ return f.str(); \ } #define X_GENERATE_FORMAT_FUNCTION_ST(z,n,d) \ template< BOOST_PP_ENUM_PARAMS(n,typename T)> \ inline std::string _fmt( \ const std::string& format, \ BOOST_PP_ENUM_BINARY_PARAMS(n,const T,& arg) ) \ { \ return _fmt( format.c_str(), BOOST_PP_ENUM_PARAMS(n, arg)); \ } #define X_GENERATE_FORMAT_FUNCTION_W(z,n,d) \ template< BOOST_PP_ENUM_PARAMS(n,typename T)> \ inline std::wstring _fmt( const wchar_t* format, BOOST_PP_ENUM_BINARY_PARAMS(n,const T,& arg) ) \ { \ boost::wformat f; \ f.exceptions( boost::io::no_error_bits ); \ f.parse(format); \ f BOOST_PP_REPEAT(n,X_GENERATE_FORMAT,_); \ return f.str(); \ } #define X_GENERATE_FORMAT_FUNCTION_W_ST(z,n,d) \ template< BOOST_PP_ENUM_PARAMS(n,typename T)> \ inline std::wstring _fmt( \ const std::wstring& format, \ BOOST_PP_ENUM_BINARY_PARAMS(n,const T,& arg) ) \ { \ return _fmt( format.c_str(), BOOST_PP_ENUM_PARAMS(n, arg)); \ } // 単一引数オーバーロード inline std::string _fmt( const char* format ){ return std::string( format ); } inline std::string _fmt( const std::string& format ){ return format; } inline std::wstring _fmt( const wchar_t * format ){ return std::wstring( format ); } inline std::wstring _fmt( const std::wstring& format ){ return format; } // 関数本体生成マクロを繰り返し呼び出す。 BOOST_PP_REPEAT_FROM_TO( 1, 10, X_GENERATE_FORMAT_FUNCTION, _) BOOST_PP_REPEAT_FROM_TO( 1, 10, X_GENERATE_FORMAT_FUNCTION_ST, _ ) BOOST_PP_REPEAT_FROM_TO( 1, 10, X_GENERATE_FORMAT_FUNCTION_W, _) BOOST_PP_REPEAT_FROM_TO( 1, 10, X_GENERATE_FORMAT_FUNCTION_W_ST, _) #undef X_GENERATE_FORMAT_FUNCTION #undef X_GENERATE_FORMAT_FUNCTION_ST #undef X_GENERATE_FORMAT_FUNCTION_W #undef X_GENERATE_FORMAT_FUNCTION_W_ST #undef X_GENERATE_FORMAT