Boost.Serialization - BOOST_CLASS_VERSION
以下のコードを書いたとする.
namespace foo { class bar { // ... private: // serialize の実装 }; BOOST_CLASS_VERSION(bar, 1); }
すると、以下の様なエラーが大量に出てくる.
error: 'foo::boost::**' has not been declared
どうやらこれはBOOST_CLASS_VERSIONをグローバル名前空間に書かないと起こるようである.
実装を読んでいないのではっきりとは言えないが、BOOST_CLASS_VERSIONを用いる際は名前空間の外にだそう.
namespace foo { // ... } BOOST_CLASS_VERSION(foo::bar, 1);
using と using namespace
Twitterでこういうコードを見かけた。
#include <utility> namespace N { using namespace std; class C {}; void swap(C&, C&) {} void f() { int n1 = 1; int n2 = 2; swap(n1, n2); // compile Error } } int main() { N::f(); return 0; }
これはコンパイルエラーになる。だが、using namespace std;を using std::swapにすると正しく動く。それはなぜか。
using declaration(using std::swap)は現在のコンテキスト内にswapを提供する(この場合N内)が、using directive(using namespace std)はグローバルnamespace、すなわちglobal namespace(::)にstd名前空間内の変数やら関数が提供される。
コードに戻ろう。もし仮にusing namespace std;としていた場合を考える。swap(n1, n2)が呼ばれたタイミングでは、まずnamespace N内のswapを照会する。そして、今回それが見つかる。こうなるとここでLookupはおしまい、つまりglobal namespaceまでswapを見に行かないので、std::swapがそもそも候補に入ってない。結果、swap -> C& -> C& -> void が呼ばれてCompile Errorになる。
using std::swapとすれば、std::swapがN名前空間内に提供されるので、std::swapが正しく呼び出される。なのでコンパイルが通る。
確かこんな挙動だったと思いますが、間違ってたら誰か教えてください。
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 使うときはオーバーロードはやめよう」