2010-04-28 124 views
3

我有一個特別ornery一段網絡代碼。我正在使用asio,但這對這個問題並不重要。我認爲除了關閉它之外,沒有辦法解除綁定套接字。問題是open(),bind()listen()都可以拋出system_error。所以我用一個簡單的try/catch來處理代碼。代碼寫在破碎的。複雜的錯誤處理

using namespace boost::asio; 

class Thing 
{ 
public: 

    ip::tcp::endpoint m_address; 

    ip::tcp::acceptor m_acceptor; 

    /// connect should handle all of its exceptions internally. 
    bool connect() 
    { 
     try 
     { 
     m_acceptor.open(m_address.protocol()); 
     m_acceptor.set_option(tcp::acceptor::reuse_address(true)); 

     m_acceptor.bind(m_address); 
     m_acceptor.listen(); 

     m_acceptor.async_accept(/*stuff*/); 
     } 
     catch(const boost::system::system_error& error) 
     { 
     assert(acceptor.is_open()); 
     m_acceptor.close(); 
     return false; 
     } 
     return true; 
    } 

    /// don't call disconnect unless connect previously succeeded. 
    void disconnect() 
    { 
     // other stuff needed to disconnect is ommited 
     m_acceptor.close(); 
    } 
}; 

的錯誤是,如果插座連接失敗,它會嘗試關閉它在catch塊和亂扔關閉從未被打開的受體另一SYSTEM_ERROR。

一個解決方案是在catch塊中添加一個if(acceptor.is_open()),但味道不對。有點像混合C -style錯誤檢查與c++例外。如果我去哪條路線,我不妨使用open()的非投擲版本。

boost::system::error_code error; 
acceptor.open(address.protocol, error); 
if(! error) 
{ 
    try 
    { 
     acceptor.set_option(tcp::acceptor::reuse_address(true)); 

     acceptor.bind(address); 
     acceptor.listen(); 

     acceptor.async_accept(/*stuff*/); 
    } 
    catch(const boost::system::system_error& error) 
    { 
     assert(acceptor.is_open()); 
     acceptor.close(); 
     return false; 
    } 
} 
return !error; 

是否有使用RAII和try/catch塊來處理這些可能出現的異常優雅的方式?

我只是錯了頭試圖避免if(error condition)風格錯誤處理時使用異常?

回答

3

我建議只是在做不同的處理錯誤open,因爲有不同的清理之前和之後:

bool connect() 
{ 
    try { 
    m_acceptor.open(m_address.protocol()); 
    } catch(const boost::system::system_error& error) { 
    return false; 
    } 

    try { 
    m_acceptor.set_option(tcp::acceptor::reuse_address(true)); 

    m_acceptor.bind(m_address); 
    m_acceptor.listen(); 

    m_acceptor.async_accept(/*stuff*/); 
    } catch(const boost::system::system_error& error) { 
    m_acceptor.close(); 
    return false; 
    } 
    return true; 
} 
+1

這包括兩個try-catch塊,其價格昂貴並且不符合RAI C++習慣用法。 – 2010-05-02 18:06:54

2

有了try-catch,你可以考慮到system_error有一個error_code,它給了你真正的原因。所以你可以在catch語句上測試這個error_code。

要使用RAI你需要做連接的構造函數和析構函數斷開,但我不知道背後是什麼

acceptor.async_accept(/*stuff*/); 

所以也許你需要讓出這部分。 Thing th;

{ 
Connector conn(th);/connect on constructor 
// ... th.async_accept 
// do some work while connected 
} 
// disconnect on destructor 

連接器將採取的受體是否打開或不使用特定的成員變量只是在acceptor.open後置IS_OPEN()成功護理。

Connector::Connector(...) 
    : ... 
    , is_open(false) 
    { 
    m_acceptor.open(m_address.protocol()); 
    is_open=true; 
    m_acceptor.set_option(tcp::acceptor::reuse_address(true)); 

    m_acceptor.bind(m_address); 
    m_acceptor.listen(); 

    m_acceptor.async_accept(/*stuff*/); 
    } 

    Connector::~Connector(...) 
    { 
    // other stuff needed to disconnect is omitted 
    if (is_open) m_acceptor.close(); 
    } 
+0

這實質上是移動'如果(IS_OPEN)''來〜Connector'代替把它留在'connect()'函數中。其中一個優點是,如果我需要在別處打開接受器,它可以避免代碼重複。在我的特殊情況下,我沒有。 – 2010-04-28 16:46:31

+1

那麼,代碼沒有try-catch塊。你沒有發現這是一個優勢?您要求RAI如何簡化您的代碼,建議可能會更簡單嗎? – 2010-05-01 10:04:24