C++相談室 part165
!extend:checked:vvvvv:1000:512 !extend:checked:vvvvv:1000:512 ↑同じ内容を3行貼り付けること 次スレは>>980 が立てること 無理なら細かく安価指定 ※前スレ C++相談室 part164 https://mevius.5ch.net/test/read.cgi/tech/1683600652/ VIPQ2_EXTDAT: checked:vvvvv:1000:512:: EXT was configured てきました㌧クス、 iosヘッダで探せば良かったのか、、、 C++23だとこれ std::print("{:+}", x); PythonやC#のようなf"{x}"構文が作られる事はないんだろうか >>85 リフレクションが充実すればその上に実装できるので 直接的な言語機能として用意すると 辻褄合わせが後で面倒になるだろうし、 現時点で考えるのは時期尚早だと思う。 質問なのですが自作クラスFooのストリーム出力演算子に引数付きのマニピュレータを追加したいのですが ↓こんなやつ Foo obj; cout << custom_setw(10) << obj; // 整数のベクトル的なオブジェクトobjの要素を幅10文字で出力 custom_setw()で与えた10という数値をつつがなくFooのストリーム出力演算子 std::ostream& operator<<(std::ostream& os, const Foo& obj) に渡すには一体どうすれば……orz グローバル変数渡しは最初に思いつくのですが、ostringstream os1, os2とFoo obj1, obj2に対して異なるスレッドで os1 << custom_setw(30) << obj1; // スレッド1 os2 << custom_setw(20) << obj2; // スレッド2 とかやったら詰むし std::setw()とか一体どうやってるんじゃ…… >>87-88 特定の型を出力するときにだけ作用する書式を設定するマニピュレータってこと? std::setw がやってるのはストリームのメンバ関数 width を呼ぶのと同じ。 ストリームのオブジェクトが幅に関する情報を保存するデータメンバを持っていて、それを変更してる。 もちろんあなたが定義した独自の型 (この例では Foo) に結び付いた書式を保持するところなんて存在しないから単純にはできない。 私が思いつくのは ・ ストリームのほうも新しいものを定義する、つまり basic_ostream を継承して Foo 用の書式を格納するデータメンバを増やす ・ ストリームに対応付いたデータを格納するものを std::map かなんかで保持しておいて Foo を出力するときはそれを参照する ということくらいかな。 スレッドが絡むと面倒くさいけど、しょうがないね。 既存のcoutとかstringstreamとかで使いたいなら、グローバルのstd::unordered_map<std::ios_base*, MyManipData>にでも置いとくしかないだろうね ストリームオブジェクトの状態で持たせるクソ設計が悪いんだ レス㌧クス、 >ストリームのオブジェクトが幅に関する情報を保存するデータメンバを持っていて、それを変更してる。 なるほど…… これはユザーの立場からはメンバの追加ができない領域なので、Fooの側にメンバを持たせることにしますか…… class FooWrp { const Foo& m_objRef; int m_nWidth; public: FooWrp(const Foo& obj, int width) : m_objRef(obj), m_nWidh(width) { } friend std::ostream& operator<<(std::ostream& os, const FooWrp& wrp) { ...(wrp.m_objRefの要素をwrp.m_nWidthに描かれている幅で出力する処理)... } }; // 使い方 Foo obj; cout << FooWrp(obj, 10); ダッサwwwwwwwwwwwwwwwwwww しかし、「たかが表示の整形」のために排他制御しつつmapを弄ったりTLSの利用を考えるのもアレなのでこれはこれで仕方が無いのか、orz ストリーム用の演算子なんか作らずに オブジェクトの文字列表現を返す関数を用意したほうが汎用性高くない? Foo obj; std::string s = obj.str(10); // 10億ギガ文字 cout << s; 出力を完遂できるんかこれ…… C++20 からは std::formatter を特殊化して書式指定を解釈するコードを入れておけば std::format で独自の型を表示しようとしたときにその特殊化が使われる仕組みになってる。 オブジェクトの文字列表現が10億ギガ文字もあるの!? FooWrpオブジェクトをうっかりログに出力しちゃったら大変だね!! 実験せずに質問するますが、 int a, b; cin >> &x >> &y; に対し、 Q1. 「100 a」を入力したら例外もタイムアウト待ちも発生せず、cin.fail()がtrueになるだけ? Q2. 「100」とだけ入力してそのまんま(リダイレクト元のファイルハンドルか何かが タイムアウトもエラーもクローズもしなければ)ならそれっきり返ってこない? EOFが抜けてたorz Q2のケースにおいて「100 [EOF]」なら(多分)cin.fail()でとりあえずすぐ返ってくるのかそうか、 >>97 実にしょうもない確認なんだけど、 言いたいことは int x, y; cin >> x >> y; でよいんだよね? >>99 >int x, y; >cin >> x >> y; おk スマンカッタorz エラーとEOFのどちらかを検知したかったら!cin.good()が正しいっぽい? https://blog.emattsan.org/entry/20110819/1313743195 ここまでは分かった気がするが、 ストリーム入力演算子std::istream& operator>>(std::istream& os, Foo& obj)の中で入力に失敗した場合 どうやってエラーを呼び出し元に通知したらええんじゃ…… クラスFooの入力ストリーム演算子の中で整数を2個読むとして、 std::istream& operator>>(std::istream& os, Foo& obj) { int x, y; os >> x >> y; if (!os.goot()) { return os; // エラー発生時は単純にreturn os; でおk? } obj.m_x = x; obj.m_y = y; return os; } >>100 ストリームのフラグを立てるメンバ関数は setstate だけど >>101 の状況ならフラグはもう立ってるから戻るだけで問題ないよ。 わかりた ていうかエラー判定(eofbit以外のビットのセットを判定)なら!os.good()や!os.goot()ではなくて os.failed()か!osですたね……orz これらに関して追加の質問が出たら初心者スレに書くわサーセン;;; class A{ char buf_[size]; } このbuf_に任意のオブジェクトをplacement newして使用するのだけど このオブジェクトをコピーしたりムーブする場合、単純にコピー元のbuf_からコピー先のbuf_にmemcopyしてしまって大丈夫ですか? POD は削除された。 trivially copyable の要件を満たすならその型は memcpy でコピーしてもよい。 std::is_trivially_copyable で判定できるのでどこかに制約を入れておけば安心かもね。 削除されたんですか?(バージョンいくつで?) c++20 で非推奨になった、てのはすぐ調べられたんだけどその先が分からんです >>109 規格の文面としては C++20 で POD はもう削除されていて POD の概念を使わない形で再編されてる。 std::is_pod はまだある (非推奨) からこれが POD の定義だという意味ではまだあるとも言えるけど。 >>104 まず自分でコード書いてみ よろしくないところは指摘してやるから POD が削除されたかどうかは重要ではなくて、 POD より弱い制約 (trivially copyable) で memcpy が許されるというのが主旨。 その点は C++11 からそうなってる。 「構造体」ってのも C++ 用語的にはイケてないと思うよ。 アライメントは基本型のどれかと同じアライメント制約に合わせれば良いいということなら 使おうとしているうちで最も厳しいアライメント制約に一致する基本型を含むunionで解決するというのがC言語における伝統的な手法という印象、 C++でunionにしたら(それが可能なメンバのみなら) memcpyは安全とかにはならない? >>116 コンストラクタや代入演算子がトリビアルであることなどの制約を守れば共用体も trivially copyable になりうる。 (C++ の共用体はコンストラクタやメンバ関数を定義できるがそこらが制限されることになる。) >>112 とりあえず書いてみたけどどうですかね? template<std::size_t buf_size> struct A { private: struct base_ { virtual ~base_() {}; }; template <typename F> struct derived_ : base_ { F f_; derived_(F f) : f_{ std::move(f) } {} }; base_* p_; alignas(alignof(std::max_align_t)) uint8_t buf[buf_size]={0}; public: A() :p_(nullptr) {}; template <typename F> void assign(F f) { if (p_ == nullptr) { p_ = ::new (buf) derived_<F>{std::move(f)}; } } //コピーコンストラクタ A(A& src) { p_ = ::new (buf) decltype(src.*p_); //ここが怪しい }; }; decltype(src.*p_)ってbase_なのでダメだろう ていうか、decltypeで型指定してるだけだからコピーもなにもされてないぞソレ A::base_に以下を足してA::derived_で実装し Aのコピーコンストラクタから呼べば? virtual base_ *clone (void *p) = 0; void*って、ポインターの先のサイズ未知だよなぁ derived_かそのメンバf_の型を知るにはRTTIしかないのですかね? >>121 decltypeだと派生先の型はわからないのか >>122 実体のコピーの処理が抜けてました… >>124 こんな感じです? struct base_ { virtual base_* clone (void *p) = 0; virtual ~base_() {}; }; template <typename F> struct derived_ : base_ { F f_; derived_(F f) : f_{ std::move(f) } {} base_* clone (void *p_buf){ return new(reinterpret_cast<derived_<F>*>(p_buf))(f_); } }; A(A& src) { p_ = src.clone((void*)buf); }; derived_(F f) ←この時点でムダなコピーが1度発生していることには気付いてる? バッファの中にオブジェクトを作れたら、それで何をしたいのかが気になる >>127 cloneの中ってplacement newでcopy constructorを呼ぼうとしてるんだよな? いちおうあってるけどundefined behaviorまみれ たぶんやりたいことは std::allocator_traits::construct なのかな 例外って全部mainで捕捉すべきかな? 調べてみたら例外が捕捉されずにプログラムが終了する場合スタックアンワインドが起こるかは実装定義みたいなんだけど、それじゃグローバルなオブジェクトのデストラクタが呼ばれないんじゃないかって思って試してみたのよ。 https://ideone.com/wSLZfL やっぱりデストラクタは呼ばれなかったからリソースリークが起こりうるんじゃないかと思うんだけど、例外に対してはどういう態度でいるべきかな? A. リソースリークはまずい。だから例外は全部捕捉するべき。 B. 例外はロジック上捕捉する必要があるものだけ捕捉して、それ以外はほっといていい。 C. 例外が捕捉されなければstd::abortが呼ばれるので、コアダンプなりで色々調べることもできる。だからmainで例外を全部握りつぶすようなことはすべきではない。 D. 時と場合による。 例外時の挙動とか仕様とか調べてるうちに頭ぐるぐるしてわけわかんなくなってきた そもそもプログラムが終了してリソースリークするのかな? メモリー、ファイルハンドル、ソケット、ミューテックスなどのリソースはOSが責任持って解放するよね どのようなリソースがリークしますか? >>133 すべてじゃね? それらの選択肢は別に排他的な選択肢じゃないかと OS管理なリソースはアプリの終了なんか知らないってのもあるからなぁ 得にドライバ関連とかな >>136 あれ、少なくともAとCは排他的だと思うんだけど 全部の選択肢を選ぶとすると具体的にはどうなるのかな 今どきのOS使ってたらOSリソースはリークしない まぁプロセスがゾンビになるのはよくあるが そもそもアプリ的にデータの不整合とか出るから論外だろう ファイルやなんかの外部データ使わないなら関係ないだろうけど よくあるのは異常終了時にファイルをフラッシュしておきたいとかだろ 汎用的にこれを実現するのは結構むずい あとコアダンプの観点では例外飛ばさずに即死したほうがいい エラーだからって一時ファイル山盛り残して放置しないでください 質問した者だけど 確かに近代的なOSであればリソースの始末はよしなにやってくれるだろうし、「絶対にデストラクタが呼ばれなきゃ困る」って状況でもなければいちいちすべての例外を捕捉する必要はないのかな(毎回ボイラープレートコードみたいに書くのもやだし) 例外処理って、メモリ破壊やファイルシステム破壊みたいな絶望的な状況を想定しなきゃいけないんだよ。 ファイルに何か書き込んだら他のファイルを壊しちゃうかもしれない、みたいな。 だからファイル関連の操作をしていいのは、ファイルシステム周りの無事を確信できるときだけ。 データを上書き保存とかしていいのは、データとファイルシステム両方の無事を確信できるときだけ。 何も確信できないときは、何もせずに墜ちなきゃいけない。 ってことで例外機構はデフォルトで何もせずに異常終了するようになってるんだよ。 理想的には全ての例外はキャッチされるべき。 ただ、現実は理想的ではない。 キャッチするのは対処するためなので想定漏れで思ってもなかったような例外が上がってきた (対処が出来てない) ならそれはバグなんだから検証して修正する必要があるわけだし、検証しやすい形で止まったほうがいい。 C++ ではスタックの巻き戻しの途中で例外を送出したときの挙動は未定義なので通例ではデストラクタから例外を投げないように設計される。 つまりデストラクタでの後始末に失敗したらもうそれを (例外機構の仕組みでは) フォローできない。 想定されてない例外が上がってるときに後始末がちゃんとできずにわけのわからない動作を引き起こしたら検証にも支障がある。 じゃあ「投げられうるすべての例外に適切な対処ができるのが理想的だが、対処しきれない例外は投げられっぱなしにする(そしてプログラムを即座に異常終了させる)方が、思考停止でとりあえず捕捉しておくよりはまだマシ」ってことになるのかな みんなありがとう いやいや、ちゃんとデバッグしろよ こんなやつとは絶対一緒に仕事したくない ライブラリ書くときはライブラリで対処できない例外は握り潰さずに上位で伝搬させろ!と言われてるよね アプリも同じだと思う 明確に対処すべきことがあるなら例外をキャッチすればいいし ないならそのままプロセス落としてOSに任せればいいんでない? 投げられっぱなしにするって言い方が不適切だったかな、別に例外のせいでプログラムが実際に異常終了するのを見ても知らんぷりするって意味じゃないよ 実際にプログラムが異常終了したんだったらその都度原因は突き止めて修正するし、そもそもすべての例外に網羅的に対処するのが現実的なときは最初からそうするよ 想定される状況には対処しているならどこで想定漏れがあるかはやってみないとわからない。 経験を蓄積し続けるしかしょうがないんだよな。 蓄積がテストケースの形などになっているとより良いと思う。 自分の知らないライブラリの奥底からいつ投げられるかわからない例外なんて対処しようがない かつスタックアンワインドしたらデバッグの手がかり消えるだけ やっぱC++の例外は悪…… 構造化例外ならwindbgでコアダンプを開いて!analyze -vで発生源を調べられる(仕組みは知らん がC++の例外は例外オブジェクトが持ち出した情報が全て…… という印象…… やっぱ例外というブツは、 アプリケーション領域においてプログラミがいろんなリソースを取り扱うようになった結果、 C言語流に関数の戻り値で起こり得る全てのエラーを網羅してチェックする方法が 現実的でなくなってきたから設けられたブツなので >自分の知らないライブラリの奥底からいつ投げられるかわからない例外(>>154 すなわち設計に対して想定外の事象が起きた知らせとしてが飛んでくるというのが基本的かつ本来的な姿 起こりえる全ての例外の処置を書かなければ設計とは言えない主義の人(>>149 )は 寧ろ例外の使用をやめてC言語的な書き方で全てのエラーをチェックすべき (他人様が作ったライブラリが例外を飛ばしてくるのは仕方が無いから全てcatchする つなみにOSの内部ではすべてのリソースをOSが管理する前提なので例外の出る幕は無い OSの設計に対して想定外の事象がOS内部で起きるとかあり得ないじゃない レトロな(しかしメジャーな)OSがC++ではなくC言語で書かれ続けるのはそういう理由 何が返ってくるかわからん (ドキュメント化されていない) なら返却値の形になっていても正しく対処できないのは同じだろ。 例外だとテストしないドキュメントにも書かないというモラルハザードがおきやすいとは言えるだろう 返り値はエラー体系を自分で定義しないといかんからそうはなりにくい まぁそれでも失敗を安易にfalseに丸めるどアホは多いけど >>157 >すなわち設計に対して想定外の事象が起きた知らせとしてが飛んでくるというのが基本的かつ本来的な姿 それ一般にはリリース後に起きちゃいけないことでは。プロダクトにもよるが防ぐ努力は必要だと思うがね。 >>138 全選択肢を同時に選ぶって意味に捉えられちゃったかな? そうじゃなくて、その選択肢自体が同時に適用すべきレベルのものじゃないと思うの 例外をキャッチするって決めたなら、そこには目的があるよね? 設計手順としては目的を決めてから例外を使おうって判断になるわけ その目的次第だよね?っていうのがD 目的がリソースリーク防止ならA Aのような目的を達成するために、目的範囲内でB デバッグ目的ならC 製品等で客の目に見せたくないなどの営業目的があるならCはダメで、のべつまくなしBというのもあるかもしれない 大きな目で全工程トータルを考えると全部の選択肢を適用する必要があるし、適用のしどころが違うと思うってのが>>136 の真意でした >>148 これが正しいかどうかはおいといて、人命に関わらないなら、自分はその考え方に賛成! 完璧にデバッグしろというのは自動車と医療機器など人命にかかわるものだね 重要度に応じて工数のかけ方が変わってくるので、すべてのソフトウエアで一概にこうしなさいとは言えないかな そういう意味ではD >>162 ああ、なるほどね 分かりやすくありがとう、助かりました 人命にかかわらない場合であっても、末端の関数が投げる例外の種類を見落としただけでプログラム全体が いきなり落ちるのは勘弁してほしいし、それを防ぐために全部の関数が投げる例外の種類を全部把握するというのも 無理ゲーに近い。 なので適当なレイヤーごとにざっくり std::exception をキャッチする造りにしてるな。例外の種類で選択したりはしない。 何するか思い付かないならPG辞めろ 貴様には向いてない >>167 そのtryブロックの処理が失敗したものとして処理を続ける。 んでテストケース爆増の話は?処理の具体例次第で話が変わったりするとか? 依存関係のあるものに影響あるのは当たり前 原因不明の例外起こっても突き進むんだったら擬似的にそのケース作ってテストするわな普通は 出し渋るなら別にださなくていいよ 参考にならなさそうだし それはいったい何のテストなんだろう。原因不明の例外を首尾よくキャッチできるかどうかテストしろと言っているんだろうか。 そもそもそういう例外を想定するなら、スルーして影響範囲が広がる方がテストは厄介になると思うがなあ。 たとえば業務用のラベルプリンターでAPIが提供されてるとか とりあえず try ~ catch して「印刷中に不明なエラーが発生しました」みたいなまとめかたはあるかなー すみません。質問させて下さい。次のコードがMinGW-w64 g++ v13.2では --itrでエラーになります。わからないのはVisual studio 2022およびVisual StudioがサポートするClang LLVMでは通って正しく 実行しているように見えます。--itrの仕様がないのはMinGW-w64 g++v13.2が正しいのでしょうか?それともVisual Studioが正しいのでしょうか? #include <iostream> #include <unordered_set> using namespace std; int main() { unordered_set<int> us = { 1,2,3,4,5,6 }; for(auto itr = begin(us); itr != end(us); ++itr) cout << *itr << endl; auto itr = us.begin(); ++itr; ++itr; cout << *itr << endl; --itr; cout << *itr << endl; cin.get(); return 0; } MinGW-w64 g++ v13.2のエラー // error: no match for 'operator--' (operand type is 'std::__detail::_Node_iterator<int, true, false>') >>177 unordered_set は forward iterator をサポートしているという規定がある。 https://timsong-cpp.github.io/cppwp/n3337/unord.set.overview#1 forward iterator は進めることは出来ても戻ることはできない。 この場合は operator-- がないのが正しい。 拡張してより高機能なライブラリを提供しても仕様に反するってわけではないけど マイクロソフトのドキュメントでも特に拡張しているという文面は見当たらないから あんまりあてにしないほうが良さそうには思う。 https://learn.microsoft.com/en-us/cpp/standard-library/unordered-set-class?view=msvc-170#iterator >>173 横やが関数foo()で1つの例外が発生したらその時点のfoo()呼び出しに至る10個かそこらの関数が中断されるわけや すなわち関数bar()が 処理A→B→C→D→return の順で処理が進むことを気体しているところに、Bで呼び出している関数baz()がfoo()を呼び出している結果、foo()で例外を生じると 処理A→B→return という処理順に変更になる。こうなっても大丈夫なようにtry { 処理B } catch ((fooが投げる例外)& e) { (eに対する適切な処置) } を書かねばならず、 これが実は潜在的には処理A、B、C、Dのどこでも起き得るから厳密なことを言えば全てについて書かねばならず、 それがfoo()呼び出しに至る10個かそこらの関数それぞれについて行われねばならない。 検証もそんだけ組み合わせが増える。 というわけでそんなの現実には不可能なので、現実的な処置としては ・ライブラリ製作者はなんか都合が悪い事が起きたら何でも呼び出し元に返す。 第1選択としてはエラーステータスを返すことを検討し、それではコードが煩雑になりすぎる場合は例外を投げて異常を知らせる ・ライブラリを使う人はライブラリが例外を投げない条件で使うことを心掛け、自前のコード内でtry { } catch() { }を極力しない という処置になるわ けや catchしようがしまいが、例外が起きて 処理A→B→return となるのは同じだと思うが。 その場合の検証が必要だというならどっちも必要だろ。 read.cgi ver 07.5.5 2024/06/08 Walang Kapalit ★ | Donguri System Team 5ちゃんねる