boost::filesystemを使ってある拡張子のファイルの先頭行に文字列を
ちょっと必要になったので書いてみただけです。><
#include <iostream> #include <string> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> #include <boost/xpressive/xpressive.hpp> void replace_file( const boost::filesystem::path& dir, const std::string& inset_str) { try { namespace fs = boost::filesystem; fs::recursive_directory_iterator end; for (fs::recursive_directory_iterator it(dir); it != end; ++it) { if (!fs::is_directory(*it)) { using namespace boost::xpressive; sregex r = sregex::compile( ".*\\.cpp|.*\\.h" ); smatch m; if (regex_search(it->string(), m, r)) { std::string file_data(inset_str+"\n"); fs::ifstream ifs(*it); std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(), std::back_inserter(file_data)); ifs.close(); fs::ofstream ofs(*it, std::ios::trunc); std::copy(file_data.begin(), file_data.end(), std::ostreambuf_iterator<char>(ofs)); } } } } catch (std::exception& e) { std::cout << e.what() << std::endl; } } int main(int argc, char* argv[]) { std::locale::global(std::locale("")); if (argc < 2) { std::cout << "invalid argment" << std::endl; std::cout << "replacefile2 arg1 arg2" << std::endl; std::cout << "arg1: replace files path replace *.cpp|*.h" << std::endl; std::cout << "arg2: insert string" << std::endl; return 0; } replace_file(argv[1],argv[2]); return 0; }
コンパイルしてできた実行ファイルをinsertstr4frontfiles.exeとすると。
insertstr4frontfiles.exe c:\src "// 一行目にコメントを追加"
こんな感じで実行します。
この例だと、c:\src配下の任意の.cppと*.hファイルの先頭行に"// 一行目にコメントを追加"が追記されます。
ps.あんまりスマートじゃないやり方かもだけどとりあえずこれで動く。
ちょっとだけクロージャを作ってみるのメモ
#include <iostream> #include <vector> #define CLOSURE_(A0) CLOSURE_DEF(##A0, __LINE__) #define CLOSURE_DEF(A0, id) CLOSURE_IMPL(A0, id) #define CLOSURE_IMPL(A0, id)\ struct Closure_##id {\ typedef decltype(A0) Arg0;\ Arg0& A0;\ Closure_##id(Arg0& a0) : A0(a0){} #define DEF_ operator() #define CLOSURE_EXIT_ } int main(int argc, char** argv) { int x = 5; CLOSURE_(x) int DEF_(int i) { return x + i; } CLOSURE_EXIT_ f(x); std::cout << f(5) << std::endl; std::vector<int> vi; CLOSURE_(vi) void DEF_(int i) { vi.push_back(i); } CLOSURE_EXIT_ push(vi); push(1); push(2); push(3); push(4); return 0; }
出力
10
C++03でも使えるようにするにはdecltypeのところを書き換えなきゃ。どうだったかな・・・。BOOST_TYPEOFの実装みるか。
あとf(x)ってxをCLOSURE_(x)とで2回書いてるのもダサい。どっちかだけにできないかなぁ。
追記:id:faith_and_braveさんとこで書いてたあれも書けた。
N2855 noexceptについて
C++0xの新しい提案でN2855(Rvalue References and Exception Safety)では、noexceptというキーワードが追加されています。
まずはコードから見ます。
noexcept int foo(int); int bar(int); noexcept void wibble(int x, int y) { x = foo(x); // OK: foo()は例外を投げない y = bar(y); // ERROR: bar()は例外を投げることが出来る try { y = bar(y); } catch (...) { y = 0; } // OK: 全ての例外はすでに捕捉されている。 }
このように、noexceptで修飾された関数は、例外を投げない事を静的に保障するための機能です。
なぜこのような提案が出たのかと言うと、moveコンストラクタが例外を投げると困ると言う所から来ています。
moveコンストラクタが例外を投げた場合に不味い事をstd::vectorのpush_back()を例に上げています。
std::vector::push_back(x)で保存領域を拡張してxをコンテナに追加する場合の例です。
T* reallocate(T *old_ptr, size_t old_capacity) { // #1: 新しいストレージを確保 T* new_ptr = (T*)new char[sizeof(T) * old_capacity * 2]; // #2: 新しいストレージに要素を移すことを試します unsigned i = 0; try { // #2a: 古い要素を右辺値としてあつかい古いストレージから対応する要素を新しいストレージに初期化します。 for (; i < old_capacity; ++i) new (new_ptr + i) T(std::move(old_ptr[i])); // "move" operation } catch (...) { // #2b: 新しいストレージに作ったコピーを破棄します for (unsigned v = 0; v < i; ++v) new_ptr[v]->~T(); delete[]((char*)new_ptr); throw; } // #3: 古いストレージを開放します。 for (i = 0; i < old_capacity; ++i) old_ptr[i]->~T(); delete[]((char*)old_ptr); return new_ptr; }
Tがコピーコンストラクタを持ちmoveコンストラクタを持たないクラスの場合、要素の移動は以下のように、
上記コード内の#2aでコピーコンストラクタによって行われます。
コピー元
|
||||||||||||||||
|
||||||||||||||||
コピー先
|
Tがmoveコンストラクタを持ったクラスの要素の移動は#2aでは以下のように元の要素を破壊して移動してしまうため、
コンテナの状態を例外を送出する前に復元することが出来なくなります。
コピー元
|
||||||||||||||||
|
||||||||||||||||
コピー先
|
noexceptには続きがあるのですが、続きは別な日に書きます。
※最新のドラフトN2914にはまだ追加されてないので、採用が決まったのか知っている方が居ましたら教えてください。
補足:
コード中の#2aのT(std::move(old_ptr[i]))がコピーコンストラクタを呼び出すのはreference collapsionが適用されるため。
練習でstd::reference_wrapperがラップしてる型を取得するメタ関数を作ってみた
std::reference_wrapper
std::reference_wrapper
#include <functional> #include <tuple> namespace mpl { template<class T> struct remove_wrap; { typedef T type; }; template<class T> struct remove_wrap<std::reference_wrapper<T>> { typedef T type; }; } // namespace mpl // "remove_ref.hpp"
こうなりました。
まぁとりあえずこれを無理やり使ってみます。
#include "remove_ref.hpp" #include <type_traits> // std::add_lvalue_reference template <class T> struct wrap2lvalue_ref { typedef T type; }; template <class T> struct wrap2lvalue_ref<std::reference_wrapper<T>> { typedef typename std::add_lvalue_reference<typename remove_wrap<T>::type>::type type; }; } // namespace mpl #include <string> #include <iostream> #include <cxxabi.h> using namespace mpl; template <class... Args> class Foo { std::tuple<Args...> args_; public: Foo(Args... args) : args_(args...) { } void operator()() { std::get<0>(args_) = "ABCDEF"; } }; template <class... Args> Foo<typename wrap2lvalue_ref<Args>::type...> make_foo(Args... args) { return Foo<typename wrap2lvalue_ref<Args>::type...>(args...); } int main(int argc, char** argv) { std::string str("Hello World"); std::cout << str << std::endl; auto f = make_foo(std::ref(str)); f(); // str = "ABCDEF" std::cout << str << std::endl; // "ABCDEF" }
std::reference_wrapperのデストラクタが呼ばれるコストを早々と避けたい時に使えるかも?
追記:map要らなかったmake_fooの時点でwrap2lvalue_refを適用すればよかったですね。
template <class... Args> class Foo { std::tuple<Args...> args_; public: Foo(Args... args) : args_(args...) { } void operator()() { std::get<0>(args_) = "ABCDEF"; } }; template <class... Args> Foo<typename wrap2lvalue_ref<Args>::type...> make_foo(Args... args) { return Foo<typename wrap2lvalue_ref<Args>::type...>(args...); }
ユーザ定義リテラルまとめ
このエントリは見事に間違ってたので忘れてください。><
グローバルな名前空間でもユーザ定義リテラルは定義できます。
参考 http://cpplover.blogspot.com/2009/09/user-defined-literal.html
ところで新たな疑問があったのでここにメモ。
template <char... C> std::string operator "" _c(); int main() { L"ABCDE"_c; // これはtemplate <char... C> std::string operator "" _c()でどう展開される? }
わからない・・・。
ユーザ定義リテラルを使用したい場合はどうやら名前空間を作ってそこに書かなければいけないようです。
つまりこう。
namespace A { constexpr long double operator "" _km(long double distance) { return distance * 1000; } } int main() { { using namespace A; // or using A::operator "" _km; long double meter = 1.2_km; } return 0; }
名前空間Aの_kmを使用する事を保障するためには、上記のようにスコープを切ってusing-directiveかusing-declarationで宣言しておく必要があります。
これはC++を規格を読んで書こうと言う人であれば気づくので問題ではありません。
しかし、一般のbetter CとしてしかC++を使っていない人はコンパイルが通って動きさえすればそれで良いのです。
その場合以下のようなコードが世の中に広まってしまうのではないかと不安です。
constexpr long double operator "" _km(long double distance) { return distance * 1000; } int main() { long double meter = 1.2_km; return 0; }
これはおそらくほとんどの環境で動作するでしょう。
しかしこれはC++標準の規格違反であり処理系や、ライブラリで_kmが定義されusing-directiveで
ライブラリの名前空間を宣言したとたんにコンパイルが通らなくなるでしょう。
例えば以下は曖昧さが残るためコンパイル不可
namespace A { constexpr long double operator "" _km(long double distance) { return distance * 1000; } } constexpr long double operator "" _km(long double distance) { return distance * 1000; } int main() { using namespace A; long double meter = 1.2_km; // _kmは曖昧でありエラー_kmは処理系で定義されるかもしれない return 0; }
曖昧さが残らないように書くためには以下のようにグローバル名前空間にユーザ定義リテラルは定義せずに必ず名前空間を作りその中に定義すること。
namespace A { constexpr long double operator "" _km(long double distance) { return distance * 1000; } } namespace B { constexpr long double operator "" _km(long double distance) { return distance * 1000; } } int main() { { using A::operator "" _km; long double meter = 1.2_km; } { using B::operator "" _km; long double meter = 1.2_km; } return 0; }
これで曖昧さが無くなりコンパイルエラーを引き起こすことも無く使用することが出来ます。
using namespace A;ではなく、using A::operator "" _km;で書いた理由は処理系でグローバル名前空間に_kmが定義された場合に
曖昧さを引き起こすため、これを回避するためです。
参考
ユーザ定義リテラルはclassのfriendとして宣言することもできます。
constexprをつけない場合、ユーザ定義リテラルがコンパイル時に解析されるかどうかは処理系依存です。
追記:typoの指摘をしていただいたのでコードのusing宣言部を修正しました。
C++テンプレートテクニック
やっとレビューを書きます。id:faith_and_braveさんとεπιστημηさんの共著
C++テンプレートテクニック
この本はタイトルの通りC++のテンプレートを使ったテクニック集になってます。
対象の読者は中級以上といったところでしょうか。
STLを使ってプログラミングが出来るようになった人が次のステップに進むのに必要な本だと思います。
「C++ってC言語にクラスの概念が追加されただけでしょ?」
って思ってる人には是非この本を読んでその考えが間違っていることを実感してほしいですw
テンプレートオンリー本ですが以下の本で挫折した人でも安心して読める内容です。
Modern C++ Design―ジェネリック・プログラミングおよびデザイン・パターンを利用するための究極のテンプレート活用術 (C++ In‐Depth Series)
この本(C++テンプレートテクニック)を読んでテンプレートを使ったプログラミングがもっと広まってくれると良いなぁと思います。
良書なので是非手に入れることをお勧めしたいと思います。
# Modern C++ Design読む前に欲しかったよー。
mpl勉強モード
enable_ifを使ってテンプレートメンバー関数を特殊化してみた?
#include <iostream> #include <string> #include <typeinfo> #if defined(__GNUC__) #include <type_traits> #include <cxxabi.h> #endif #if !defined(__GNUC__) template <bool b, class T> struct enable_if; template <class T> struct enable_if<true, T> { typedef T type; }; #else using std::enable_if; #endif #define DECRER_IS_TYPE(name, index) \ template <size_t N>\ struct name\ {\ static const bool value = false;\ };\ \ template <>\ struct name<index>\ {\ static const bool value = true;\ }; DECRER_IS_TYPE(is_hoge_x, 0) DECRER_IS_TYPE(is_hoge_y, 1) DECRER_IS_TYPE(is_hoge_s, 2) #undef DECRER_IS_TYPE struct hoge { int x_; double y_; std::string s_; hoge() : x_(0), y_(1.0), s_("xyuyux") {} template <size_t N> typename enable_if<is_hoge_x<N>::value, int>::type get() const { return x_; } template <size_t N> typename enable_if<is_hoge_y<N>::value, double>::type get() const { return y_; } template <size_t N> typename enable_if<is_hoge_s<N>::value, std::string>::type get() const { return s_; } }; int main(int argc, char** argv) { hoge h; std::cout << std::showpoint; std::cout << h.get<1>() << std::endl; std::cout << h.get<0>() << std::endl; std::cout << h.get<2>() << std::endl; return 0; }
実行結果
1.00000
0
xyuyux