っていうか今 規制されたんですが. あーうー.
最近 Twitter やりすぎなので,何らかの形で自粛するべきではないかと思ってる.
テンションを上げるにはそこそこ便利なので,全面禁止は無理だろうが.
連投制限のあるクライアントを (作る|利用する),とかだろうか. アイデア募集中.
まぁとにかく,寝ます. おやすみ.
次に示される関数 implicit_cast
について考える.
#include <type_traits> #include <utility> template<class To, class From, typename std::enable_if< std::is_convertible<From, To>::value >::type* = nullptr > To implicit_cast(From && x) { return std::forward<From>(x); }
この関数は,引数として渡された値を,型 To
に暗黙変換して返すものであり,
例えば
implicit_cast<double>(0);
のように使う.
さて,この関数 implicit_cast には問題点がある.
このような例を考えよう:
auto x = 0; auto y = implicit_cast<double const&>(x);
このとき, x
の型は int
であるため,インスタンス化された implicit_cast
の実装は
double const& implicit_cast(int& x) { return std::forward<int&>(x); }
となるが,これは
double const& implicit_cast(int& x) { double const& ref = x; return ref; }
であり,((std::forward
は lvalue reference に対しては特に何もしない))
double const& implicit_cast(int& x) { double temp = x; double const& ref = temp; return ref; }
と同じ動作をする.
なぜなら, x の型は int
である一方,関数の戻り値の型は double
であり,
この二つの参照は互換性がないからだ.
見て分かる通り,この関数は,ローカル変数に対する参照を返している.
この動作は undefined behavior である. 動くかもしれないし,動かないかもしれない.
auto x = 0; auto y = implicit_cast<double const&>(x); std::cout << y << std::endl; // 0 が出力されるとは限らない,もっと言うと ここが正しく実行されるかも分からない
よって,このような implicit_cast
の定義は,決して書いてはいけない.
正しい implicit_cast
の実装は,例えば以下のようになる:
template<class To, typename std::enable_if< std::is_convertible<To&&, To>::value >::type* = nullptr > To implicit_cast( typename std::enable_if<true, To>::type x ) { return std::forward<To>(x); }
typename std::enable_if
は,引数からの型推論を避けるために必要となる.
本当は typename std::identity
と書ければ良いのだが, C++11 標準には identity は存在しないため,このような表記が必要となる.((std::common_type
を使う流儀もあったが, LWG 2141 により もはや その流儀は使えなくなった. 詳しくは http://cpplover.blogspot.jp/2013/03/commontypedecay.html を参照.))
このような実装を採用すれば,関数内の一時オブジェクトが原因でコードが壊れることもなくなるし,
implicit_cast<void*>(0)
と書くことが可能になる*1という利点もある.
従って, implicit_cast 的なものを実装する場合には,基本的には こちらの実装を採用するべきである.
ただし,この場合だと, To
がクラスの場合に無駄なコピーが作られてしまうので,
効率を考えれば,多少長くなっても
template<class To, class From, typename std::enable_if< !std::is_reference<To>::value && std::is_convertible<From, To>::value >::type* = nullptr > To implicit_cast(From && x) { return std::forward<From>(x); } template<class To, typename std::enable_if< std::is_convertible<To&&, To>::value >::type* = nullptr > To implicit_cast( typename std::enable_if<true, To>::type&& x ) { return std::forward<To>(x); }
と実装するのが妥当だろう.
また,この implicit_cast
は constexpr
や noexcept
に対応していない,
といった事を考えると,実際の実装は もう少し複雑になるが,その辺りの説明は今回は省略したい.
* * * * *
さて,このような実装を採用すると,関数内で一時オブジェクトが作られることはなくなるが,
関数の外では
auto x = 0; auto y = implicit_cast<double const&>(x);
は
auto x = 0; auto y = implicit_cast<double const&>( double(x) );
と書かれてるのと同じになったため, auto
を使わず
auto x = 0; auto const& y = implicit_cast<double const&>( double(x) );
と書いた場合には, y
は一時オブジェクトへの参照を保持することになり,上手くいかない.
この寿命問題を解決するには,そもそも implicit_cast
を使うのを諦めて
int x; double const& y = x;
と書けば良い*2が,その場合,
auto x = 0; double const& y = x; x = 23; assert( y == 23 ); // ASSERTION FAILED!
と書いた場合に,一見して y
は x
と同じオブジェクトを指しているように見えるが,
実際には別のオブジェクトであるため, x
の変更は y
に反映されず,
予想外の動作となる場合が生じる.
このような「暗黙変換の結果として作られた一時オブジェクトが,参照に束縛される」ケースは,
そのような挙動になることが一見して分かりにくいため,あまり好ましい動作とは言えない.
が,この挙動がない場合には,
void do_something( std::string const& x ); do_something("hoge");
のようなコードがコンパイルエラーになってしまうため,利便性のためには必要である.
see also: http://d.hatena.ne.jp/gintenlabo/20110309/1299688709
個人的には,関数の引数の場合のみを特別扱いにして,それ以外の場合には
暗黙変換された結果として生じた一時オブジェクトを参照に束縛することは エラーにするべきだと考えている.
どうしても一時オブジェクトを参照へ束縛したい場合には,明示的にコンストラクタを呼べばいい.
なお,先ほどの問題に対処したい場合には, std::reference_wrapper
を使って,
auto x = 0; std::reference_wrapper<double const> y = x; // error, because x is not double
と書くと良いだろう.
std::reference_wrapper
使うべし. イヤーッ!
このような暗黙の一時オブジェクト生成を防ぐために attribute を使うという方法を思いついた.
double const& implicit_cast(int const& x) { [[avoid_reference_to_temporary]] return x; // compile error! }
的な.
このネタに対しては他にも書きたいことは多いし,寝て起きた後に掘り下げてみようと思う.
std::make_shared
は基本的には効率的だけど弱点もあるよ,って話.
struct Huge { int data[1024][1024]; }; #include <memory> #include <vector> int main() { std::vector<std::weak_ptr<Huge>> vec; for( int i = 0; i < 100000; ++i ) { auto p = std::make_shared<Huge>(p); vec.push_back(p); p.reset(); // *p の寿命はここで切れるけど, weak_ptr が残ってる限りメモリ領域は解放されない // 結果,メモリを食いつぶしてしまう // std::shared_ptr<Huge> p( new Huge() ); // なら問題ない } }
殆どの実装では, std::weak_ptr
は 参照カウントを確認することで 対象のオブジェクトが破棄されたか否かを判別している.
std::make_shared
は,オブジェクトと参照カウントを一回のメモリ確保で連続した領域に確保するため,
std::weak_ptr
が残っている限り,参照カウントのメモリ領域は解放することができず,
従って,それと同時に確保されたオブジェクトのメモリ領域も解放することができない,という理屈.
一応,領域を動かさずに realloc することは可能だけど,そうしてくれる処理系ってあるんだろうか?
少なくとも標準の realloc では,領域を縮める場合でも領域移動が起きる可能性があるので無理.
また,その場合でも, allocate_shared
には対応できないので ぐぬぬ
と http://cpplover.blogspot.jp/2013/04/isexplicitlyconvertible.html のコメント書いてて思ったので,書く.
まず些細な違いとして, auto
は右辺の値の const
や参照を消し飛ばす*1,という点が挙げられる.
using T = int const; // Alias declarations; I prefer this to typedef double x = 1.2; T t(x); // decltype(t) is int const auto t = T(x); // const is removed; decltype(t) is int
using T = int const&; int x = 42; T t(x); // t is a const reference to x; decltype(t) is int const& auto t = T(x); // x is copied; decltype(t) is int
が,これは今回の記事では これ以上は言及しない.
詳しく知りたい方は http://d.hatena.ne.jp/gintenlabo/20110223/1298482597 を参照.
真に問題なのは, T がポインタの場合である:
using T = int*; double d; auto x = &d; T t1(x); // ill-formed auto t2 = T(x); // well-formed(!), because T(x) is C-style cast
上記のコードで, auto t2 = T(x);
はコンパイルエラーとはならない.
なぜなら T(x)
という式は, (T)x
同じ C-style cast だからだ.
そして,少しでも C++ を知っている人であれば あえて言うまでもない事実だが, C-style cast は邪悪である.
故に, T(x)
という表現もまた,邪悪である.
良心ある C++ 使いを自覚するなら,決して使ってはならない.
* * * * *
さて,この挙動は, Variadic Templates を使う場合,特に問題になる.
例えば
template<class T, class... Args> T create(Args&&... args) { return T( std::forward<Args>(args)... ); }
このような関数を作ったとき,
double d; auto p = create<int*>(&d);
この式は問題なくコンパイルが通ってしまう上,多くのコンパイラでは警告すら出ない.
この挙動は,間違いなく危険である. よって,上記の create
の定義は,面倒でも
template<class T, class... Args> T create(Args&&... args) { T t( std::forward<Args>(args)... ); return t; // t is implicitly moved (or copy is omitted by NRVO) }
と書くのが良い.((厳密に言うと T
が const 修飾されてたり参照だったりする時には上手く行かないので,そのようなケースには別個 対応する必要がある))
もしくは, uniform initialization を使って
return T{ std::forward<Args>(args)... };
と書く手もある.
ただ,これは T
が参照の場合には上手くいかないし,
{} による初期化は () による初期化とは微妙に挙動が違うので,厳密には同じ動作にはならないことは留意するべきである.
(が,細かいことを気にしないならば {} で統一するのは有力である. 少なくとも大怪我はしない.)
また,上記の create
を SFINAE に対応させる場合,
template<class T, class... Args> auto create(Args&&... args) -> decltype( T( std::forward<Args>(args)... ) );
と書くと問題になるので,じゃあ一体どうすればいいのか,と思うかもしれないが,*2
標準には まさにその T t( std::forward
の可非を判定するために
std::is_constructible
というメタ関数が存在しているため,
template<class T, class... Args, typename std::enable_if< std::is_constructible<T, Args...>::value >::type* = 0 > T create(Args&&... args) { return T( std::forward<Args>(args)... ); }
と書けば良い.
副次的な利点として,こう書けば不正なケースは SFINAE で除外されるため,
上記のように 直接 T( std::forward
を返すことで, constexpr
にも対応できるようになる.
C++11で新たに加わった Perfect Forward は,非常に便利な機能である.
template<class T> void f(T && x) { g(std::forward<T>(x)); // 別の関数 g に処理を丸投げする }
しかしこれは,多重定義された関数で使われた場合,予期せぬ挙動を示すことがある.
例を見てみよう. http://ideone.com/TxoeLv
#include <iostream> template<class T> void f(T &&) { std::cout << "f(T &&)\n"; } void f(std::string const&) { std::cout << "f(std::string const&)\n"; } int main() { std::string x; f(x); std::string const y = ""; f(y); }
結果:
f(T &&) f(std::string const&)
この例では,同じ std::string
型の値に対して,同じ関数 f
が呼ばれたにも関わらず,
const
の有無により,実際に呼ばれる関数の種類が変化している.
ここでは具体的に書くことはしないが, Perfect Forward を使っていると,それ以外のケースでも ミスが かなり起きやすい.
よって,少なくとも,以上のコードが何故そのような挙動になるかを説明できない人は,
多重定義された関数で Perfect Forward を使ってはいけない.
多重定義されていない関数では比較的マシだが,それでも寿命問題など落とし穴は多い.
Perfect Forward 自体, C++ の規格に 相当 熟達しているという自信がない限りは,扱うべきではないだろう.
この機能はライブラリ実装者向けのものだ.
GitHub では ちょっと前から, pull request を merge したとき,その pull request を投げた
topic branch (仮に origin/hoge
とする)を Web 上の操作で簡単に削除できるようになってます
(つまり git push origin :hoge
を自動で実行してくれます).
これ,便利なんですが,手元のリポジトリに origin/hoge
への参照が残ってしまいます.
別に残ったからといって困るものでもないのですが,なんとなくキモいので削除したい.
でも,これを一々 手で削除するんじゃ手間は変わらないし,なんか手っ取り早い方法が欲しいところ.
で,調べたところ,そういう場合は
git fetch origin --prune
を使えば,リモートのリポジトリで削除された branch を削除してくれるようです.
参考:
--prune
オプションは付けて損する類のものではないようなので,デフォでそっちを使うのもアリかも.
仕事で Jenkins を使ったCI環境を立ち上げたので,メモがてら手順を書いておく.
環境は さくらのVPS, OS は Debian 6.
内容的には,セキュリティの設定が完了して実際にプロジェクトを立ち上げられるまで.
参考: https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins+on+Ubuntu
Debian 系の場合は apt-get だけで全てやってくれる.
wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add - sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list' sudo apt-get update sudo apt-get install jenkins
これだけでサーバが勝手に立ち上がってくれる. 楽ちん.
ブラウザで http://your-server-address:8080 にアクセスして Jenkins が立ち上がっていることを確認.
もちろん your-server-address の部分は置き換える.
今のままだと Web 経由で誰でも自由に Jenkins をいじれるので,セキュリティを設定する.
いろいろな方法があるが,今回は Github OAuth Plugin を使った.
これを使えば独自にアカウントを管理しなくていいし, GitHub とも連携しやすくなる.
まずプラグインを導入するが, GitHub OAuth Plugin の最新版(0.13)はエンバグしているため,普通にインストールしても動かない.
そこで,必要なプラグイン( GIT Plugin や GitHub Plugin , GitHub API Plugin 辺り)をインストールし,
ついでに既存のプラグインをアップデートした後に,
https://issues.jenkins-ci.org/browse/JENKINS-16492 にある Workaround を参考に, 0.12 をインストールする.
sudo service jenkins stop sudo rm -rf /var/lib/jenkins/plugins/github-oauth* sudo -u jenkins curl http://updates.jenkins-ci.org/download/plugins/github-oauth/0.12/github-oauth.hpi -o /var/lib/jenkins/plugins/github-oauth.jpi sudo service jenkins start
こんな感じ.
GitHub OAuth Plugin の 0.13 はバグってたが,最新バージョンでは修正されたため,
普通に「プラグインの管理」からインストールするのが楽.
インストールの際は,依存パッケージである GitHub API Plugin の他,
GIT Plugin や GitHub Plugin などもインストールしておくとよい.
次は GitHub の OAuth を使うためにアプリケーションを登録する.
https://github.com/settings/applications/new にアクセスし, Application Name は適当に Jenkins 辺りを設定,
Main URL には http://your-server-address:8080 を,
Callback URL には http://your-server-address:8080/securityRealm/finishLogin を,
それぞれ設定(もちろん your-server-address は適切に置き換える).
登録したら Client ID と Client Secret を確認.
あとは「Jenkinsの管理」から「グローバルセキュリティの設定」を選択,
「セキュリティを有効化」をチェックして「Github Authentication Plugin」を選択,
先ほど入手した Client ID と Client Secret を入力し,一旦保存.
保存したら右上のリンクからログインが出来るようになってるので,ログインする.
その後,「グローバルセキュリティの設定」から「Github Commiter Authorization Strategy」をチェック,
「Admin User Names」に管理権限を付与したい GitHub アカウント名をカンマ区切りで記述し,保存.
別のブラウザを使うなどして GitHub からログアウトした状態でアクセスし,アクセス出来ないことを確認する.
あとは適当にプロジェクト立ち上げて試行錯誤してください.
GitHub pull request builder plugin とか便利ですよ.
GitHub Plugin の Web-hook 機能を使う場合には, Github Commiter Authorization Strategy で
「Grant READ permissions for /github-webhook」をチェックする良いです.
GitHub OAuth Plugin の 0.13 がバグっている件ですが,
現在はパッチが当たったため,普通にインストールしても問題ないようです.
こちらでは まだ試してないので,実際に上手くいくかは分かりません.
追ってレビューしたいと思います.