私は他の誰かによって書かれたこのコードに出くわしました。この条件演算子の使用は推奨されていますか、それとも一般的に使用されていますか?メンテナンスが難しいと感じていますが、それとも私だけですか?これを書く別の方法はありますか?
exp_rsp_status = req.security_violation ? (dis_prot_viol_rsp && is_mstr) ?
uvc_pkg::MRSP_OKAY : uvc_pkg::MRSP_PROTVIOL : req.slv_req.size() ?
((is_mst_abort_rsp && dis_mst_abort_rsp) ||
((req.slv_req[0].get_rsp_status()==uvc_pkg::MRSP_PROTVIOL) && dis_prot_viol_rsp) ||
(is_mst_abort_rsp && req.is_pci_config_req() && dis_pcicfg_mst_abort_rsp)) ?
uvc_pkg::MRSP_OKAY : req.slv_req[0].get_rsp_status() : uvc_pkg::MRSP_OKAY;
それはただひどいコードです。
それで、あなたは何ができますか?
if
)を使用します。return
sを使用してください。深いインデントが好きな人はいません。そして明確にするために:三項演算子には何も問題はありません。慎重に使用すると、消化しやすいコードが生成されることがよくあります。ただし、ネストは避けてください。コードが非常に明確な場合は、時々2番目のレベルを使用しますが、それでも括弧を使用するので、貧弱な脳は演算子の優先順位を解読するために余分なサイクルを実行する必要がありません。
コードの読者に注意してください。
おそらくこれはデバイスドライバーのメッセージループにあり、おそらく10年前の元のコーダーは、コードのジャンプを望んでいませんでした。彼のコンパイラがジャンプ付きの三項演算子を実装していないことを彼が確認したことを願っています!
コードを調べると、私の最初の意見は、三項演算子のシーケンスは、すべてのコードと同様に、適切にフォーマットされていると読みやすくなるということです。
そうは言っても、OPの例を正しく解析したかどうかはわかりません。従来のネストされたif-else構造でさえ、検証するのは難しいでしょう。この式は、基本的なプログラミングパラダイムである分割統治法に違反しています。
req.security_violation
? dis_prot_viol_rsp && is_mstr
? uvc_pkg::MRSP_OKAY
: uvc_pkg::MRSP_PROTVIOL
: req.slv_req.size()
? is_mst_abort_rsp && dis_mst_abort_rsp
|| req.slv_req[0].get_rsp_status()==uvc_pkg::MRSP_PROTVIOL
&& dis_prot_viol_rsp
|| is_mst_abort_rsp && req.is_pci_config_req() && dis_pcicfg_mst_abort_rsp
? uvc_pkg::MRSP_OKAY
: req.slv_req[0].get_rsp_status()
: uvc_pkg::MRSP_OKAY;
リファクタリングしたときにコードがどのように見えるかを確認したかったのです。確かに短くはありませんが、話す関数名が意図を明確にする方法が好きです(もちろんここで推測しました)。変数名はおそらくグローバルではないため、関数にパラメーターが必要になるため、これはある程度疑似コードであり、コードが再び不明確になります。ただし、パラメータは、ステータスやリクエスト構造などへの単一のポインタである可能性があります(dis_prot_viol_rsp
などの値が抽出されています)。異なる条件を組み合わせるときに三項を使用するかどうかは議論の余地があります。私はそれがしばしばエレガントだと思います。
bool ismStrProtoViol()
{
return dis_prot_viol_rsp && is_mstr;
}
bool isIgnorableAbort()
{
return is_mst_abort_rsp && dis_mst_abort_rsp;
}
bool isIgnorablePciAbort()
{
return is_mst_abort_rsp && req.is_pci_config_req() && dis_pcicfg_mst_abort_rsp;
}
bool isIgnorableProtoViol()
{
return req.slv_req[0].get_rsp_status()==uvc_pkg::MRSP_PROTVIOL && dis_prot_viol_rsp;
}
eStatus getRspStatus()
{
eStatus ret;
if( req.security_violation )
{
ret = ismStrProtoViol() ? uvc_pkg::MRSP_OKAY : uvc_pkg::MRSP_PROTVIOL;
}
else if( req.slv_req.size() )
{
ret = isIgnorableAbort()
|| isIgnorableProtoViol()
|| isIgnorablePciAbort()
? uvc_pkg::MRSP_OKAY
: req.slv_req[0].get_rsp_status();
else
{
ret = uvc_pkg::MRSP_OKAY;
}
return ret;
}
最後に、uvc_pkg::MRSP_OKAY
が一種のデフォルトであり、特定の状況でのみ上書きされるという事実を利用できます。これにより、ブランチが削除されます。コードの理由を少し彫った後、どのように見えるかを見てください。セキュリティ違反でない場合は、実際のリクエストステータスから、空のリクエストと無視できるアボートを除いたものを確認してください。
eStatus getRspStatus()
{
eStatus ret = uvc_pkg::MRSP_OKAY;
if( req.security_violation )
{
ret = ismStrProtoViol() ? uvc_pkg::MRSP_OKAY : uvc_pkg::MRSP_PROTVIOL;
}
else if( req.slv_req.size()
&& !isIgnorableAbort()
&& !isIgnorablePorotoViol()
&& !isIgnorablePciAbort()
)
{
ret = req.slv_req[0].get_rsp_status();
}
return ret;
}
なんて醜い混乱。私はそれをifに分解し、それ以外の場合はそれが何をしているかを確認するだけです。あまり読みにくいですが、とにかく投稿したいと思いました。うまくいけば、他の誰かがあなたのためにもっとエレガントな解決策を持っています。しかし、あなたの質問に答えるために、それほど複雑な三元を使用しないでください。それが何をしているのかを理解するために私がしたことを誰もやりたくない。
if ( req.security_violation )
{
if ( dis_prot_viol_rsp && is_mstr )
{
exp_rsp_status = uvc_pkg::MRSP_OKAY;
}
else
{
exp_rsp_status = uvc_pkg::MRSP_PROTVIOL;
}
}
else if ( req.slv_req.size() )
{
if ( ( is_mst_abort_rsp && dis_mst_abort_rsp ||
( req.slv_req[0].get_rsp_status() == uvc_pkg::MRSP_PROTVIOL && dis_prot_viol_rsp ) ||
( is_mst_abort_rsp && req.is_pci_config_req() && dis_pcicfg_mst_abort_rsp ) )
{
exp_rsp_status = uvc_pkg::MRSP_OKAY;
}
else
{
exp_rsp_status = req.slv_req[0].get_rsp_status();
}
}
else
{
exp_rsp_status = uvc_pkg::MRSP_OKAY
}
これはひどいコードです。
変数を単一の式で初期化することが望ましい場合がよくありますが(たとえば、const
にすることができます)、これはこのようなコードを書く言い訳にはなりません。複雑なロジックを関数に移動し、それを呼び出して変数を初期化できます。
void
example(const int a, const int b)
{
const auto mything = make_my_thing(a, b);
}
C++ 11以降では、ラムダを使用して変数を初期化することもできます。
void
example(const int a, const int b)
{
const auto mything = [a, b](){
if (a == b)
return MyThing {"equal"};
else if (a < b)
return MyThing {"less"};
else if (a > b)
return MyThing {"greater"};
else
throw MyException {"How is this even possible?"};
}();
}
他の人は、そのコードの抜粋がどれほどひどいのか、素晴らしい説明ですでに言っています。そのコードが悪い理由をもう少し説明します。
1つの「if-else」を1つの機能だけを実装すると考えると、そのコードがどれほど複雑かは明らかです。あなたの場合、私はifの数を数えることさえできません。
あなたのコードが 単一責任の原則 を破って壊れていることは明らかです。
...クラスまたはモジュールには、変更する理由が1つだけある必要があります。
ユニットテストは悪夢であり、これは別の危険信号です。そして、あなたの同僚はそのコードの単体テストを書き込もうとさえしなかったに違いありません。
一般的または推奨?番号。
私は似たようなことをしましたが、理由がありました:
リリースされることのなかったのはDEBUGCODEでした。
textprintf_ex(gw->GetBackBuffer(), font, 0, 16, WHITE, -1, "BUTTON: %s",
//If... Then Display...
(ButtonClicked(Buttons[STOP]) ? "STOP"
: (ButtonClicked(Buttons[AUTO]) ? "AUTO"
: (ButtonClicked(Buttons[TICK]) ? "TICK"
: (ButtonClicked(Buttons[BLOCK]) ? "BLOCK"
: (ButtonClicked(Buttons[BOAT]) ? "BOAT"
: (ButtonClicked(Buttons[BLINKER]) ? "BLINKER"
: (ButtonClicked(Buttons[GLIDER]) ? "GLIDER"
: (ButtonClicked(Buttons[SHIP]) ? "SHIP"
: (ButtonClicked(Buttons[GUN]) ? "GUN"
: (ButtonClicked(Buttons[PULSAR]) ? "PULSAR"
: (ButtonClicked(Buttons[RESET]) ? "RESET"
: /*Nothing was clicked*/ "NONE"
)))))))))))
);
If-elseチェーンを使用しなかった唯一の理由は、Wordを画面に印刷するだけだったため、コードが膨大になり、追跡が困難になったためです。