我需要在 C++ 中创建“事务流”。我所说的“事务流”是指在处理过程中出现错误时会倒回的流。例如,如果流的使用者以某种方式无法处理流的数据,我希望流在生成该数据之前恢复到其状态。
也许一个惰性流会实现这一点?这是具有通用解决方案的常见情况,还是我必须为我的特定问题编写自己的自定义实现?
我需要在 C++ 中创建“事务流”。我所说的“事务流”是指在处理过程中出现错误时会倒回的流。例如,如果流的使用者以某种方式无法处理流的数据,我希望流在生成该数据之前恢复到其状态。
也许一个惰性流会实现这一点?这是具有通用解决方案的常见情况,还是我必须为我的特定问题编写自己的自定义实现?
好吧,首先想到的是将范围接口(用于惰性和可组合性)与事务接口(用于回溯)结合起来:
#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 将范围接口与事务接口分离也可能是有益的。不过,如果不求助于虚拟功能,我现在想不出一个好方法。
如果您在 Windows 上工作,则有一种称为结构化存储的东西,它提供了一种事务形式。您可能必须将问题分解为单独的流。
这里有更多信息: http: //msdn.microsoft.com/en-us/library/windows/desktop/aa380369 (v=vs.85).aspx