make_append_tuple を書いた
template の練習がてら書いてみようと思ったらウンコードが完成した。
tupleをくっつけるだけにしようと思っていたのだが、面白く無いのでめちゃくちゃにしてやった。
後悔はしていない。
まずはそれを見て貰いたいと思う。
#include <iostream> #include <type_traits> #include <tuple> namespace mpl { template <bool, typename T1, typename T2> struct typeif_c { using type = T1; }; template <typename T1, typename T2> struct typeif_c<false, T1, T2> { using type = T2; }; template <bool B, typename T1, typename T2> struct eval_typeif_c : typeif_c<B, T1, T2>::type {}; template <class M, typename T1, typename T2> struct typeif : typeif_c<M::value, T1, T2> {}; template <class M, typename T1, typename T2> struct eval_typeif : typeif_c<M::value, T1, T2>::type {}; template <typename> struct is_tuple : std::false_type {}; template <typename... Types> struct is_tuple<std::tuple<Types...>> : std::true_type {}; template <typename...> struct append_tuple_impl { using type = append_tuple_impl; }; template <typename T> struct append_tuple_impl<T> { using type = typename typeif_c< is_tuple<T>::value, T, std::tuple<T> >::type; }; template <typename T, typename... Args> struct append_tuple_impl<T, Args...> : append_tuple_impl<std::tuple<T>, Args...> {}; template <typename... Types> struct append_tuple_impl<std::tuple<Types...>, append_tuple_impl<>> { using type = std::tuple<Types...>; }; template <typename... Types1, typename... Types2> struct append_tuple_impl<std::tuple<Types1...>, std::tuple<Types2...>> { using type = std::tuple<Types1..., Types2...>; }; template <typename... Types1, typename T, typename... Types2> struct append_tuple_impl<std::tuple<Types1...>, T, Types2...> : append_tuple_impl<std::tuple<Types1..., T>, typename append_tuple_impl<Types2...>::type> {}; template <typename... Types1, typename... Types2, typename... Types3> struct append_tuple_impl<std::tuple<Types1...>, std::tuple<Types2...>, Types3...> : append_tuple_impl<std::tuple<Types1..., Types2...>, typename append_tuple_impl<Types3...>::type> {}; template <typename T> using remove_r_t = typename std::remove_reference<T>::type; template <typename... Types> struct append_tuple : append_tuple_impl<remove_r_t<Types>...> {}; template < index_t... N, typename... Types, typename T > auto make_append_tuple_helper1( std::tuple<Types...>&& t, T&& a, index_tuple<N...> ) -> decltype( std::make_tuple( std::get<N>( t )..., a ) ) { return std::make_tuple( std::get<N>( t )..., a ); } template < index_t... N, typename... Types, typename T > auto make_append_tuple_helper1( std::tuple<Types...>& t, T&& a, index_tuple<N...> ) -> decltype( std::make_tuple( std::get<N>( t )..., a ) ) { return std::make_tuple( std::get<N>( t )..., a ); } template < index_t... N, typename... Types, typename T > auto make_append_tuple_helper2( T&& a, std::tuple<Types...>&& t, index_tuple<N...> ) -> decltype( std::make_tuple( a, std::get<N>( t )... ) ) { return std::make_tuple( a, std::get<N>( t )... ); } template < index_t... N, typename... Types, typename T > auto make_append_tuple_helper2( T&& a, std::tuple<Types...>& t, index_tuple<N...> ) -> decltype( std::make_tuple( a, std::get<N>( t )... ) ) { return std::make_tuple( a, std::get<N>( t )... ); } template < typename... Types1, typename... Types2, index_t... N, index_t... M > auto make_append_tuple_helper3( std::tuple<Types1...>&& t1, std::tuple<Types2...>&& t2, index_tuple<N...>, index_tuple<M...> ) -> decltype( std::make_tuple( std::get<N>( t1 )..., std::get<M>( t2 )... ) ) { return std::make_tuple( std::get<N>( t1 )..., std::get<M>( t2 )... ); } template < typename... Types1, typename... Types2, index_t... N, index_t... M > auto make_append_tuple_helper3( std::tuple<Types1...>& t1, std::tuple<Types2...>& t2, index_tuple<N...>, index_tuple<M...> ) -> decltype( std::make_tuple( std::get<N>( t1 )..., std::get<M>( t2 )... ) ) { return std::make_tuple( std::get<N>( t1 )..., std::get<M>( t2 )... ); } template < typename T1, typename T2 > auto make_append_tuple_helper( T1&& t1, T2&& t2 ) -> decltype( std::make_tuple( t1, t2 ) ) { return std::make_tuple( t1, t2 ); } template < typename... Types, typename T > std::tuple<Types..., remove_r_t<T>> make_append_tuple_helper( std::tuple<Types...>& t, T&& arg ) { return make_append_tuple_helper1( std::forward<std::tuple<Types...>>( t ), std::forward<T>( arg ), index_range<0, sizeof...(Types), 1>{} ); } template < typename... Types, typename T > std::tuple<Types..., remove_r_t<T>> make_append_tuple_helper( std::tuple<Types...>&& t, T&& arg ) { return make_append_tuple_helper1( std::forward<std::tuple<Types...>>( t ), std::forward<T>( arg ), index_range<0, sizeof...(Types), 1>{} ); } template < typename T, typename... Types > std::tuple<remove_r_t<T>, Types...> make_append_tuple_helper( T&& arg, std::tuple<Types...>& t ) { return make_append_tuple_helper2( std::forward<T>( arg ), std::forward<std::tuple<Types...>>( t ), index_range<0, sizeof...(Types), 1>{} ); } template < typename T, typename... Types > std::tuple<remove_r_t<T>, Types...> make_append_tuple_helper( T&& arg, std::tuple<Types...>&& t ) { return make_append_tuple_helper2( std::forward<T>( arg ), std::forward<std::tuple<Types...>>( t ), index_range<0, sizeof...(Types), 1>{} ); } template <typename... Types1, typename... Types2> std::tuple<Types1..., Types2...> make_append_tuple_helper( std::tuple<Types1...>&& t1, std::tuple<Types2...>&& t2 ) { return make_append_tuple_helper3( std::forward<std::tuple<Types1...>>( t1 ), std::forward<std::tuple<Types2...>>( t2 ), index_range<0, sizeof...(Types1), 1>{}, index_range<0, sizeof...(Types2), 1>{} ); } template <typename... Types1, typename... Types2> std::tuple<Types1..., Types2...> make_append_tuple_helper( std::tuple<Types1...>& t1, std::tuple<Types2...>& t2 ) { return make_append_tuple_helper3( std::forward<std::tuple<Types1...>>( t1 ), std::forward<std::tuple<Types2...>>( t2 ), index_range<0, sizeof...(Types1), 1>{}, index_range<0, sizeof...(Types2), 1>{} ); } template <typename... Types1, typename... Types2> std::tuple<Types1..., Types2...> make_append_tuple_helper( std::tuple<Types1...>& t1, std::tuple<Types2...>&& t2 ) { return make_append_tuple_helper3( std::forward<std::tuple<Types1...>>( t1 ), std::forward<std::tuple<Types2...>>( t2 ), index_range<0, sizeof...(Types1), 1>{}, index_range<0, sizeof...(Types2), 1>{} ); } template <typename... Types1, typename... Types2> std::tuple<Types1..., Types2...> make_append_tuple_helper( std::tuple<Types1...>&& t1, std::tuple<Types2...>& t2 ) { return make_append_tuple_helper3( std::forward<std::tuple<Types1...>>( t1 ), std::forward<std::tuple<Types2...>>( t2 ), index_range<0, sizeof...(Types1), 1>{}, index_range<0, sizeof...(Types2), 1>{} ); } template <typename T> auto make_append_tuple( T&& t ) -> decltype( std::make_tuple( std::forward<T>( t ) ) ) { return std::make_tuple( std::forward<T>( t ) ); } template <typename... Types> std::tuple<Types...> make_append_tuple( std::tuple<Types...>& t ) { return t; } template <typename... Types> auto make_append_tuple( std::tuple<Types...>&& t ) -> decltype( std::move( t ) ) { return std::move( t ); } template <typename T1, typename T2, typename... Args> typename append_tuple<T1, T2, Args...>::type make_append_tuple( T1&& t1, T2&& t2, Args&&... args ) { return make_append_tuple( make_append_tuple_helper( std::forward<T1>( t1 ), std::forward<T2>( t2 ) ), std::forward<Args>( args )... ); } } int main() { auto tuple1 = std::make_tuple( 5, "hoge", true ); // tuple<int, const char*, bool, float> auto tuple2 = mpl::make_append_tuple( tuple1, 1.5f ); std::cout << std::get<0>( tuple2 ) << std::endl; // tuple<float, int, const char*, bool, float> auto tuple3 = mpl::make_append_tuple( 1.5f, tuple1 ); std::cout << std::get<0>( tuple3 ) << std::endl; // tuple<int, const char*, bool, int, const char*, bool, float> auto tuple4 = mpl::make_append_tuple( tuple1, tuple2 ); std::cout << std::get<4>( tuple4 ) << std::endl; // tuple<int, int, int> なぜかmake_tupleみたいな使い方もできる auto tuple5 = mpl::make_append_tuple( 1, 2, 3 ); std::cout << std::get<2>( tuple5 ) << std::endl; // tuple<bool, int, int, int, double> auto tuple6 = mpl::make_append_tuple( false, tuple5, 2.2 ); std::cout << std::get<4>( tuple6 ) << std::endl; }
(´・_・`)(´・_・`)(´・_・`)
なんというウンコード。計算量なんて目も当てられないレベルである。
(計算してみてあまりにもひどかったので言わない)。
気が向いた方はもっと効率よく実装してみてください。
僕はたとえ改善案が思いついたとしてもこのコードには触りたくないです。では
【追記】
@kester44 さんがもっとわかりやすい実装を書いてくださいました。
template <typename T> struct is_tuple : std::false_type {}; template <typename ... Elements> struct is_tuple<std::tuple<Elements ...> > : std::true_type {}; namespace detail { template < typename T, typename std::enable_if<is_tuple<typename std::decay<T>::type>::value>::type* = nullptr > inline T wrap_tuple( T && x ) { return std::forward<T>( x ); } template < typename T, typename std::enable_if<!is_tuple<typename std::decay<T>::type>::value>::type* = nullptr > inline std::tuple<T> wrap_tuple( T && x ) { return std::make_tuple( std::forward<T>( x )); } } // END: namespace (detail) template <typename ... Elements> inline auto make_append_tuple( Elements && ... elements ) ->decltype( std::tuple_cat( detail::wrap_tuple( std::forward<Elements>( elements )) ... )) { return std::tuple_cat( detail::wrap_tuple( std::forward<Elements>( elements )) ... ); }
めっちゃ簡潔ですね。俺のコードがいかにウンコだったかお分かりいただけたと思います。
std::tuple_cat の存在忘れてたよ…つらぽよ…もうだめだ…
GCC Bug 58046 - template operator= in SFINAE class
コンパイルエラーになって欲しかったのにICEになりやがったのでバグレポ。
SFINAE 使ってたらなぜかバグりました。でも少し変えたら動くんですよねー。
詳細はリンク先を参照ください。
バグレポの書き方とか教えてもらえばよかったかなぁ…
Boost.Log 追記モード・ユーザー定義の severity
Boost.Logはファイルに書き出せるわけですが、追記モードも可能です。
ついでに、自分で定義した severity を書きだす方法もどーぞ。
#include <boost/log/utility/setup/file.hpp> #include <boost/log/utility/setup/common_attributes.hpp> #include <boost/log/trivial.hpp> #include <boost/log/attributes.hpp> #include <boost/log/expressions.hpp> #include <boost/log/support/date_time.hpp> namespace logging = boost::log; namespace attrs = boost::log::attributes; namespace keywords = boost::log::keywords; namespace sinks = boost::log::sinks; namespace expr = boost::log::expressions; enum severity_level { debug, warning, error, critical }; struct severity_tag; std::ostream& operator<< ( std::ostream& strm, severity_level level ) { static const char* strs[] = { "debug", "warning", "error", "critical" }; if ( static_cast< std::size_t >( level ) < sizeof( strs ) / sizeof( *strs ) ) strm << strs[level]; else strm << static_cast< int >( level ); return strm; } int main() { logging::register_simple_formatter_factory< severity_level, char >( "Severity" ); logging::add_file_log( keywords::file_name = "logs/%Y%m%d.log", keywords::time_based_rotation = sinks::file::rotation_at_time_point( 0, 0, 0 ), keywords::format = ( expr::stream << expr::attr< unsigned int >( "LineID" ) << ":" << "[" << expr::format_date_time< boost::posix_time::ptime >( "TimeStamp", "%Y-%m-%d %H:%M:%S" ) << "][" << expr::attr< severity_level, severity_tag >( "Severity" ) << "]: " << expr::smessage ), // 又は、単に // keywords::format = "%LineID%:[%TimeStamp%][%Severity%]:%Message%" keywords::open_mode = (std::ios::out | std::ios::app) // std::ios のがそのまま使える ); logging::sources::severity_logger<severity_level> slg; logging::add_common_attributes(); // LineID を使えるようにする logging::core::get()->add_global_attribute( "TimeStamp", attrs::local_clock() ); BOOST_LOG_SEV( slg, debug ) << "hoge"; BOOST_LOG_SEV( slg, error ) << "bar"; BOOST_LOG_SEV( slg, warning ) << "piyo"; BOOST_LOG_SEV( slg, critical ) << "foo"; }
ログファイルがこちら
1:[2013-08-02 19:15:24][debug]: hoge 2:[2013-08-02 19:15:24][error]: bar 3:[2013-08-02 19:15:24][warning]: piyo 4:[2013-08-02 19:15:24][critical]: foo
severity に関しては他にも方法あるかも。多分これが一番楽だと思います。
Boost.Logのリファレンス適当に読んだだけなので問題があったら言ってください。
C# で素数列挙した -C# 勉強中
C#の勉強中なので、まずは基本中の基本、素数の列挙をやりました。
もっとかっこよく書けるぞ!って人はコメントください。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main( string[] args ) { Enumerable.Range(2, 100) .Where(x => !Enumerable.Range(2, x - 1).Any(y => x % y == 0)) .ToList() .ForEach(Console.WriteLine); } } }
【追記】
CountじゃなくてAnyを使うようにしました。
あと、Sqrt(x)まででいいじゃんって指摘がありましたが、Rangeの第2引数が end でなく count なので、めんどくさくてそのままです。(Range(begin, end) でなく Range(begin, count)なので…
Universal Reference を知るべき複数の理由
復習第2弾。
universal reference って名前は知らなくていいので、C++のこれらの挙動だけは知っててほしいです。
前の記事と合わせてどうぞ。
Universal Reference は以下 URefs と略させて頂きます。
URefs とオーバーロード
class some_class { public: template <typename T> void f( T const& ) { std::cout << "called T const&" << std::endl; } template <typename T> void f( T&& ) { std::cout << "called T&&" << std::endl; } }; int main() { some_class sc; sc.f( 0 ); int x = 0; sc.f( x ); }
このコードの結果は、
called T&& called T const&
ではありません
正しくはこう
called T&& called T&&
もぅ C++ マジ闇。。。リスカしょ。。。
どうしてこうなった
先日(というか同日)の記事を読んだ人はわかると思いますが、type deduction が発生する場合、template <typename T> some_class::f(T&&) の "T&&" は URefs、すなわち lvalue にも rvalue にもマッチする関数となります。
つまり、さっきの some_class::f の正しい解釈は、
- const lvalue で呼び出された場合、 T const& の f が呼ばれる
- lvalue(not const) または rvalue で呼び出された場合、T&& の f が呼ばれる
となります。
これは、
class some_class { public: void f( const another_class& ); // 1 void f( another_class&& ); // 2 }; another_class make_another_class() { ... } some_class sc; another_class ac; sc.f( ac ); // 1 が呼ばれる sc.f( make_another_class() ); // 2 が呼ばれる
のように、URefs で無い場合と混同してしまうことが非常に多いですので、気をつけましょう。そうでないとプロジェクトが謎のバグで炎上します。
(というかこれ知らなかったら絶対間違えるでしょ…
URefs はとりあえずなんでも食う
std::string search_name( int id ) { ... } class person { public: template <typename T> person( T&& t ) : name( std::forward<T>( t ) ) {} person( int id ) : name( search_name( id ) ) {} private: std::string name; }; std::size_t my_id = 100; person p( my_id ); // error: invalid conversion // from ‘unsigned int’ to ‘const char*’
どうしてこうなった
タイトルに有る通り、 URefs は食いしん坊さんなのでどんな型でもとりあえずマッチします。
今回の例では、 my_id の型が unsigned int です。なので、person(int) よりも person(unsigned int&) になれる URefs のほうが呼ばれてしまいます。型気をつければいいんですけど、人間間違えるときもあるので気をつけましょう。
URefs はやっぱりなんでも食う
class some_class { public: template <typename T> some_class( T&& ) { ... } // URefs ctor template <typename T> some_class& operator=( T&& ) { ... } // URefs copy };
このようにクラスを書いたとします。
template が使われたコンストラクタ、コピー代入演算子は暗黙的に生成されるデフォルトコンストラクタとコピー(ryを抑制しませんので、結局のところ
class some_class { public: template <typename T> some_class( T&& ) { ... } // URefs ctor some_class( some_class const& ) = default; // default copy ctor some_class( some_class&& ) = default; // default move ctor template <typename T> some_class& operator=( T&& ) { ... } // URefs copy some_class& operator=( some_class const& ) = default; // default copy copy some_class& operator=( some_class&& ) = default; // default move copy
となります。
これがどんな悲劇を生むのか、まずはそのコードを見ましょう。
some_class sc; const some_class csc; some_class sc1( sc ); // 1 some_class sc2( csc ); // 2 some_class sc3( std::move( sc ) ); // 3 some_class sc4( std::move( csc ) ); // 4
「え?これ template の部分関係なくね?」と思った人がいるかもしれません。
では、1~4は実際どれを呼んでるのでしょうか。
答えはこれです!
- URefs ctor
- default copy ctor
- default move ctor
- URefs ctor
理由は最初の URefs とオーバーロードに書いてますが、気づかないとやばいです。
謎のバグで残業代も出ないのに会社に泊まり込みになるかもしれません。気をつけましょう。
URefs と initializer_list
template parameter の type deduction が std::initializer_list のときうまく働かない(auto はちゃんとうごく)問題。規格(§14.5.6.2)を見る限りなるほど、って感じですがややこしいのです。stackoverflow にあるので、気になる方は読んでみてください。
stackoverflow -Universal references and std::initializer_list-
どう立ち向かうか
「URefs 使うときはオーバーロードはやめよう」
Universal Reference is 何
universal reference という文字が見えたけど聞いたことなかったのでまとめました。
知識としては皆さんご存知だと思いますので復習程度に見ていただければ幸いです。
知らなかった人はこの記事で入門しましょう!
Scott Meyers氏による universal reference の 定義
If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.
あれ…この記事これでおしまいでいいんじゃ…?
&& の持つ意味
もう少しお付き合いください。
void f( std::vector<int>&& v ) {} // 1 template <typename T> // 2 void f( T&& t ) {} // some_class というクラスがあったとする some_class sc1; some_class&& sc2 = sc1; // 3 auto&& sc3 = sc2; // 4
結論からいうと2、4の T&&, auto&& が universal reference にあたるわけですが、ちょっくら説明いたします。
"&&" を見たら無条件で「あっ!rvalue reference だ!」となる方もいらっしゃることと思いますが、早計です。落ち着きましょう。
ここで問題になってくるのが、Tという型が deduced type である場合、単純に "T&&" と書いただけではこれは rvalue reference にも lvalue reference にもなりうる、ということです。
簡単にいえば、
- lvalue によって初期化が行われれば universal reference は lvalue reference になる
- rvalue によって初期化が行われれば universal reference は rvalue reference になる
ということです。(lvalue やら rvalue やらわからない人は僕の前の記事や、C++の言語規格を参照してください)
というわけで、元のコードをもっかい見てみましょう。
int x = 0; f( x ); // 1: x は lvalue なので、T&& は int& になる f( 0 ); // 2: 0 は rvalue なので、T&& は int&& になる // 4: sc2 は lvalue なので、some_class& sc3 = sc2; と同じ意味になる auto&& sc3 = sc2;
もうお分かりですね?
気をつけるべき universal reference
このコードは universal reference でしょうか?
template <typename T> void f( std::vector<T>&& v ) {}
答えは No. です。なぜなら "&&" が T にかかってないからです。
では、これはどうでしょう。
template <typename T> void f( T const&& t ) {}
答えは No. です。const をつけたら universal reference の要件を満たさないようです。
なので T const&& は rvalue reference になります。
じゃあこれは…? vector<T>::push_back(T&& t) の T&& は universal reference でしょうか?
template <typename T/*, class Allocator = allocator<T>*/> class vector { public: void push_back( T&& t ); };
答えは No. だそうです。なぜなら、vectorを使うときは vector<int> のように T の型がわかりますので、type deduction が発生しないからです。つまり、T&& は rvalue reference といえます。
(と言っていますが、vector<int&> にしたら T&& は lvalue reference となります。だから universal reference といってもいいんじゃないかと個人的には思っています。type deduction が発生しないので定義には当てはまらないですけどね。おそらくですが、
vector<int> v; // vector はさっき書いた vector int i = 0; v.push_back( i ); // error! i は lvalue reference
とエラーになって、T&& と書いているからといって lvalue も rvalue も渡せるわけではないということが言いたいのだと思います。)
最後にこいつはどうでしょう?
template <typename Args...> void f( Args&&... args );
答えは Yes. です。No 続きだったから No だと思った方もおられるでしょうが、これは普通に Yes でいいと思います。
universal reference の注意点とか書きつつ universal reference だったの1つだけじゃねぇかとかいうツッコミは勘弁してください。
最後に
内容的には全く新しくないわけですが、復習にはなったかと思います。
Perfect forwarding(std::forward) とか有名ですが、それにつながる知識ですので知らなかった方は知っておいて損は無いと思います。
Boost.勉強会 #12 大阪に行って来ました
はじめに
寝屋川市の大阪電気通信大学でBoost.勉強会 #12 が開催されました。
僕は発表者として行きましたが、今回もC++erがたくさん集っていたようです。
ちなみに初参加だったので、少し緊張しました。
発表
僕は「Boost.MPLをつかってみよう」という題で発表しました。
スライドにちょっと不備があったので、修正したのをこちらにあげています。
緊張して言いたかったことが多分言えてないのと、日本語がところどころおかしくなってたような気がします。内容もあんまりおもしろくなかったと自分で思ってるので、反省。あと資料はちゃんと前日までに完成させて練習しないとダメですね。申し訳ありませんでした。
発表の内容に関しましては、Boost.MPLは使う機会がないというか、あんまり恩恵を受けてないように感じる方もいらっしゃることと思います。が、普段使ってるBoostのライブラリ等、見えない所で活躍している縁の下の力持ちです。ライブラリを実装している方は、ちょっとだけ使っていることも有るでしょう。大事にしてあげてください。
Twitterで@SubaruGさんが「個人的にはテンプレートメタプログラミングは遅延評価が必要な場合以外は C++11 では使うべきではないと思う. decltype 使おう」とおっしゃられていましたが、自分にはよくわかりませんでした…
最後の補足でADLが~関数ルックアップが~という話がありましたが、ややこしい話なのでもしかしたらまたまとめるかもしれません。
あと、@andochinさんが指摘してくださった以下のコード(Numの部分がtypename又はclassの間違いでは?)ですが
template <Num N>
constexpr N min = std::numeric_limits<N>::min();
ここでのNumはconceptのconstraintsだと思ってください。Conceptの内容が混じってたのでつい書いちゃいました。指摘ありがとうございます。制約しないならtypenameかclassでOKです。
とりあえずconceptはよ
懇親会
闇の軍団ばっかりですごかったです。というか、何気なく喋っていた人が普段Twitterで見かけるすごい人だったりして面白かったです。ご飯よりも話をしてお腹いっぱいになりました。
特に@bolero_MURAKAMIさんとはかなりお話しまして、プログラミングの話だけじゃなくいろんなことを教えてくださりました。ありがとうございました。