Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optimize ternary operator

I came across this code written by someone else. Is this usage of the conditional operator recommended or commonly used? I feel it is less maintainable - or is it just me? Is there any alternate way of writing this?

  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; 
like image 829
Jean Avatar asked Oct 20 '15 14:10

Jean


People also ask

Is a ternary operator more efficient?

Ternary operators can be a more concise way in limited situations (your example is a good one). BUT they can often be abused to make code unreadable (which is a cardinal sin) = do not nest ternary operators!

Is it good practice to use ternary operator?

The conditional ternary operator can definitely be overused, and some find it quite unreadable. However, I find that it can be very clean in most situations that a boolean expression is expected, provided that its intent is clear.

Is the ternary operator branchless?

You could use a branchless ternary operator, sometimes called bitselect ( condition ? true : false). Don't worry about the extra operations, they are nothing compared to the if statement branching.

Which is faster switch or ternary operator?

There should not be a major difference in speed. However, the switch is far more readable and maintainable. The switch option is easier to debug.


2 Answers

That's just horrible code.

  • It's badly formatted. I don't see the hierarchy of the expression.
  • Even if it had good formatting, the expression would be way too complex to quickly parse with the human eye.
  • The intention is unclear. What's the purpose of those conditions?

So what can you do?

  • Use conditional statements (if).
  • Extract the sub-expressions, and store them in variables. Check this nice example from the refactoring catalog.
  • Use helper functions. If the logic is complex, use early returns. Nobody likes deep indentation.
  • Most importantly, give everything a meaningful name. The intention should be clear why something has to be calculated.

And just to be clear: There's nothing wrong with the ternary operator. If used judiously, it often produces code that's easier to digest. Avoid nesting them though. I occasionally use a second level if the code is crystal clear, and even then I use parentheses so my poor brain doesn't have to do extra cycles decyphering the operator precedence.

Care about the readers of your code.

like image 160
Karoly Horvath Avatar answered Oct 14 '22 03:10

Karoly Horvath


Perhaps this is in a device driver's message loop and the original coder, possibly 10 years ago, didn't want jumps in the code. I hope he verified that his compiler didn't implement the ternary operator with jumps!

Examining the code, my first remark is that a sequence of ternary operators is -- like all code -- better readable when adequately formatted.

That said, I'm not sure that I parsed the OP's example correctly, which speaks against it. Even a traditional nested if-else construct would be hard to verify. This expression violates the fundamental programming paradigm: Divide and Conquer.

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; 

I wanted to check how the code looks when refactored. It sure is not shorter but I like how the speaking function names make the intent clearer (of course I guessed here). This is, to some degree, pseudo code because the variable names are probably not global so that the functions would have to have parameters, making the code less clear again. But perhaps the parameter could be a single pointer to a status or request structure or such (from which values like dis_prot_viol_rsp have been extracted). Whether or not to use a ternary when combining the different conditions is up to debate. I find it often elegant.

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; } 

Finally we can exploit the fact that uvc_pkg::MRSP_OKAY is kindof the default and only overwritten under certain circumstances. This eliminates a branch. Look how after some chiseling the code's reasoning is nicely visible: If it's not a security violation look closer and check the actual request status, minus empty requests and ignorable aborts.

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()                 &&  !isIgnorableProtoViol()                 &&  !isIgnorablePciAbort()             )     {         ret = req.slv_req[0].get_rsp_status();     }      return ret; } 
like image 35
Peter - Reinstate Monica Avatar answered Oct 14 '22 03:10

Peter - Reinstate Monica