我需要在C++中创建“事务流”。我所说的“事务流”是一种流,如果在处理中的某个时刻出现错误,该流将倒带。例如,
如果流的使用者以某种方式无法处理该流的数据,我希望在生成该数据之前将流还原到其状态。

也许懒惰的流会实现这一目标?这是使用通用解决方案的常见情况,还是我必须针对自己的特定问题编写自己的自定义实现?

最佳答案

好吧,首先想到的是将范围接口(interface)(用于惰性和可组合性)与事务性接口(interface)(用于回溯)结合在一起:

#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将范围接口(interface)与事务接口(interface)解耦也可能是有益的。不过,我现在不想到一种不使用虚拟功能的好方法。

10-07 18:53
查看更多