Optimize ternary operator

后端 未结 6 1990
野的像风
野的像风 2021-02-03 18:49

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 th

6条回答
  •  情话喂你
    2021-02-03 19:46

    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()
                    &&  !isIgnorablePorotoViol()
                    &&  !isIgnorablePciAbort()
                )
        {
            ret = req.slv_req[0].get_rsp_status();
        }
    
        return ret;
    }
    

提交回复
热议问题