>
문제

다음과 같은 간단한 상황이 도처에 나타납니다. 다음과 같은 함수 서명으로 많은 수의 요청이 기기에 제공됩니다.

Err execute( const ICommandContext &context, 
         const RoutineArguments &arguments, 
         RoutineResults &results)

본질적으로 이것을 호출하는 요청 처리 서버가 있으며 이러한 서명을 가진 다양한 요청 유형에 대해 함수를 실행합니다. 오류가 발생한 경우 2 개의 반환 경로가 있습니다.

<올>
  • 와이즈 비즈  출력 유형 ( Err 와 동등한 것으로 간주하십시오. )는 요청이 아닌 시스템과 관련하여 문제가 있음을 서버 나 시스템에 알리는 데 사용됩니다. 사용자 요청이 처리되기 전에 항상 함수 상단에 정렬됩니다.
  • int   RoutineResults 를 제공합니다  요청의 실패 정보를 클라이언트에 반환하는 데 사용할 수있는 기능
  • 이러한 이유로 이런 유형의 코드가 많이 나타납니다 :

    setStatus
    
    

    우리는 // Failure due to request Err error = someFunctionCall(clientInput); if (!error.success()) { results.setStatus(error); // Inform the client of the error return SUCCESS; // Inform the system that we are all good } 가있는 특정 요청 유형을 가지고 있습니다.  시스템으로 들어오고 나가는 매개 변수. 개념적으로이 15 의 15 개가 필요합니다  낭비처럼 보이는 세트. 또한 우리가 돌아가는 방법에 대해 무엇인가를 바꿔야 할 경우 오류가 발생하기 쉽습니다.Wyzwyz를 효과적으로 위임하는 방법  함수에서 한 번만 발생하면되는 짧은 양의 코드로 돌아가시겠습니까?

    매크로 솔루션

    와이즈 비즈  시스템은 다음과 같은 매크로를 사용하여이 문제를 해결할 수 있습니다.

    if error do
    
    

    다음과 같이 사용될 것입니다 :

    setStatus
    
    

    이것은 코드를 깔끔하게 정리하지만 특히 c 에는 좋지 않습니다. . #define M_InitTry Err error #define M_Try(statement) if (!(error = statement).success()) { goto catch_lab; } #define M_Catch catch_lab: if (!error.successs()) #define M_Return return error 가 생략 할 수있는 변수를 정의하면 문제가 발생할 수 있습니다. .

    위임 솔루션

    더 많은 Err execute( const ICommandContext &context, ...) { M_InitTry; ... M_Try(someFunctionCall(clientInput)); M_Try(someFunctionCall(otherClientInput)); ... M_Catch { // Other specific actions for dealing with the return. results.setStatus(error); error = SUCCESS; } M_Return; } 를 생각하고 있었다  그래서 RAII 유형 대의원이 도움이 될 것이라고 생각했습니다. 다음과 같은 것 :

    goto
    
    
    매우 간단합니다. 소멸자에서 작업을 구현하여 함수가 반환 될 때까지 작업을 위임합니다. 다음과 같이 사용할 수 있습니다 :

    goto
    
    

    실제 예 이 솔루션은 문제가 없지만 몇 가지 문제가 있습니다.

    <올>
  • 무슨 ​​일이 일어나고 있는지 명확하지 않습니다.
  • 오류를 올바르게 설정하는 것이 실수하기 쉽습니다.
  • 여전히 많은 C++ 가 필요합니다  반품 처리에 대한 진술.
  • 종료 작업을 구성하는 기능이 좋지 않습니다.
  • 사용자가 함수 반환시 아이템의 소멸 순서를 신중하게 고려하지 않으면 위험합니다.
  • 더 나은 해결책?

    이 문제는 자주 발생합니다. 이 세트의 명확한 위임을 제공하고 유형 조치를 리턴하는 일반적인 솔루션이 있습니까?

    <시간>

    아래에 불행한 제한이 있습니다. 미래의 사람들에게 도움이 될 수 있으므로 응답하지 못하게하세요.

    <올>
  • c ++ 03 제한 시스템에서 작업하고 있습니다. 우리는 class DelegateToFunctionEnd { typedef std::function<void(void)> EndFunction; public: DelegateToFunctionEnd(EndFunction endFunction) : callAtEnd(endFunction) { } ~DelegateToFunctionEnd() { callAtEnd(); } private: EndFunction callAtEnd; }; 이지만 c ++ 11은 없습니다.
  • 내장 된 시스템에는 예외 및 메모리 할당에 대한 바보 같은 규칙이 있습니다.
  • Err execute( const ICommandContext &context, ...) { Err error; DelegateToFunctionEnd del(std::bind(&RoutineResults::setStatus, &results, std::cref(error))); error = someFunctionCall(clientInput)); if (error) return SUCCESS; ... }

    • 답변 # 1

      오류 상태 코드로 인해 문제가 발생하면 대신 예외 사용을 고려해야합니다. 즉, 함수의 API를 변경하십시오

      따라서 사후 조건으로 성공할 수 있습니다

      적당한 Wyzwyz를 던져  실패한 경우

      이렇게하면 상태 코드를 검사하는 것을 "잊어 버릴"수 없습니다. 오류 조건을 처리하지 않으면 저수준 코드에서 발생한 예외가 자동으로 위로 올라갑니다. 당신은 단지 std::exception 필요  

      다음과 같은 저수준 예외

      오류가 발생한 경우 수동 롤백 또는 할당 해제를 수행해야합니다. RAII는 실용적이지 않습니다. 이 경우, 당신은 expcetion을 다시 던질 것입니다.

      던져진 중첩 예외를 사용하여 하위 수준 예외 메시지 또는 예외 유형을 고급 메시지로 변환하려고합니다.

    • 답변 # 2

      어쩌면 다음과 같은 문장을 배열로 작성할 수 있습니다 :

      catch
      
      

      다른 진술만으로 여러 번 그렇게하면 당신은 만들 수 있습니다

      Err execute( const ICommandContext &context, ...)
      {
          const boost::function<Err()> functions[] = {
              boost::bind(&someFunctionCall, std::ref(clientInput)),
              boost::bind(&someFunctionCall, std::ref(otherClientInput)),
              // ...
          };
          for (std::size_t i = 0; i != sizeof(functions) / sizeof(functions[0]); ++i) {
              Err err = functions[i]();
              if (!err.successs()) {
                  results.setStatus(err);
                  return SUCCESS;
              }
          }
          return SUCCESS;
      }
      
      

      Err execute_functions(const ICommandContext &context, std::function<Err()> functions); 와 같은 다른 진입 점을 제공 할 수도 있습니다.  필요에 따라

    • 답변 # 3

      기능을 분리하십시오.

      내부 함수는 사용자 입력에 따라 오류 코드를 반환합니다. 바깥 쪽은이를 클라이언트 오류로 변환하고 서버 측 오류 만 반환합니다.

      내부 함수는 다음을 포함합니다 :

      OnError
      
      

      반복적으로. 외부에는 클라이언트에 대한 릴레이 오류 코드가 있지만 한 번만 있습니다.

      Err은 운영자 bool 만 있으면됩니다. 가질 수없는 경우 Err로 변환하거나 Err로 변환하고 연산자 bool이있는 유형을 작성하십시오.

    • 답변 # 4

      체크 등을 수행하는 오류에 메소드를 추가하고 bool을 리턴 할 수 있습니까?

      if(Err error = someFunctionCall(clientInput))
        return error;
      
      
      if(!someFunctionCall(clientInput).handleSuccess(results)) { return SUCCESS; }

    관련 자료

  • 이전 regex - 하나의 Perl 명령으로 여러 번 대체 할 수 있습니까?
  • 다음 Active Directory 도메인 서비스에 REST API를 사용할 수 있습니까?