2012-07-12 112 views
1

我需要在C++中創建「事務流」。我的意思是「事務性流」是一個流,如果在處理過程中某個點出現錯誤,它將倒回。例如, 如果某個流的消費者無法處理該流的數據,我希望該流在生成該數據之前恢復到其狀態。如何在C++中創建事務流?

也許一個懶惰的流可以實現這一點?這是常見解決方案的常見情況,還是我必須爲我的特定問題編寫自己的自定義實現?

+0

這將取決於流的底層實現(文件系統,網絡/協議等)。如果支持事務,並且支持所需的語義,則可以直接使用它。如果你想從中抽象出來,你必須自己推出。如果做得正確,這是不平凡的。例如,不要忘記,「事務」通常不僅需要可靠的ROLLBACK,而且還需要可靠的COMMIT(使用懶/內存支持的方法很難實現)。 – 2012-07-12 14:12:41

+0

@ Christian.K,好點。我意識到這是不平凡的。爲了以抽象的方式完成,我猜想有必要使用像事務內存這樣的東西,但是如上所述爲其他底層實現提供接口。我希望有人已經完成了這項工作(可能是Boost?)! ;) – 2012-07-12 16:13:45

+0

聽起來像設計模式的應用程序。看看紀念品... – steffen 2012-07-12 20:03:37

回答

2

好了,想到的第一件事就是一系列的接口組合(懶惰和組合性),具有交易界面(回溯):

#include <iostream> 
#include <stack> 
#include <sstream> 

struct transaction_failure {}; 

class transactional_istream_range { 

    std::istream& stream; 
    std::stack<std::streampos> states; 

public: 

    transactional_istream_range(std::istream& stream) 
    : stream(stream) {} 

    // Transaction interface. 

    template<class R, class T> 
    R transaction(R(*body)(T&)) { 
    try { 
     begin(); 
     R result = body(*this); 
     commit(); 
     return result; 
    } catch (const transaction_failure&) { 
     rollback(); 
    } 
    return R(); 
    } 

    void begin() { 
    states.push(stream.tellg()); 
    } 

    void commit() { 
    states.pop(); 
    } 

    void rollback() { 
    stream.seekg(states.top()); 
    states.pop(); 
    } 

    // Range interface. 

    bool empty() const { 
    return stream.peek() == EOF && stream.eof(); 
    } 

    char front() const { 
    return stream.peek(); 
    } 

    void pop_front() const { 
    stream.ignore(1); 
    } 

}; 

然後,您可以輕鬆地編寫工作模板功能交易範圍:

#include <cctype> 

template<class R> 
std::string parse_integer(R& input) { 
    std::string result; 
    while (!input.empty()) { 
    if (std::isdigit(input.front())) { 
     result += input.front(); 
     input.pop_front(); 
    } else { 
     throw transaction_failure(); 
    } 
    } 
    return result; 
} 

int main() { 
    std::istringstream stream("1234a"); 
    typedef transactional_istream_range tir; 
    tir input(stream); 
    std::string result = input.transaction(parse_integer<tir>); 
    std::cout << "Result: " << result; 
} 

這只是第一個近似值;您可能會避開必須指定交易功能範圍的類型(即,只需要parse_integer而不是parse_integer<...>)。以範圍形式編寫多種惰性流和惰性算法非常簡單。至於擴展這個,你可以通過參數化事務處理來調用用戶指定的提交或回滾函數,或者只是單獨實現每種類型的回滾。使用mixin將範圍界面與交易界面分離可能也是有益的。不過,我現在想不出採用虛擬功能的好方法。

+0

優秀的例子! – 2012-12-12 20:08:14