设计迭代器以在取消引用时抛出异常

时间:2014-06-04 14:26:09

标签: c++ exception c++11 throw hpx

我需要找到一种方法来在我编写的名为hpx::parallel::copy的函数中测试我的异常处理。库中的其他函数(如hpx::parallel::transform)很容易测试,因为可以传递谓词,但抛出异常,但copy不带谓词。

我认为我最好的解决方案是使用一个以某种方式引发取消引用的迭代器,虽然我不太确定如何去做这个... ......任何其他解决这个问题的建议都是受欢迎的同样。这是我的问题的代码示例

//transform.cpp , testing exceptions
bool caught_exception = false;
    try {
        base_iterator outiter = hpx::parallel::transform(policy,
            iterator(boost::begin(c)), iterator(boost::end(c)), boost::begin(d),
            [](std::size_t v) {    //easy, predicate can be passed
                throw std::runtime_error("test");
                return v;
            });

        HPX_TEST(false);
    }
    //catching exceptions etc...

//copy.cpp, testing exceptions 
bool caught_exception = false;
    try {
        base_iterator outiter = hpx::parallel::copy(policy,
            iterator(boost::begin(c)), iterator(boost::end(c)), boost::begin(d)); //no predicate... how can I throw?
        HPX_TEST(false);
    }
    //catching exceptions etc..

更具体一点,我希望能够修改我想要throw的确切内容,以便测试多个场景,这只是意味着我无法使用抛出超出范围的实现或我无法控制的其他例外情况,我需要抛出特定的例外情况。

3 个答案:

答案 0 :(得分:1)

最简单的方法是使用对它们迭代的容器的反向引用来构造迭代器。无论何时增加容器的end(),或递减其begin(),或者取消引用容器范围之外的任何内容,都会抛出异常。因为迭代器具有对容器的引用,所以您拥有所有这些信息。开销是每个迭代器的简单引用(或指针),以及operator--operator++operator*中的少量逻辑。

Microsoft在Checked Iterators中使用这种方法,您可以在使用标准库时将其打开。 implementation中提供了示例SafeSTL。例如。他们的vector<T>看起来有点像这样:

template<class T>
class vector
{
public:
    class iterator
    {
    public:
         // regular iterator interface
    private:
         std::vector<T>* owner; // used by implementation to do checking
    };

    // rest of vector<T> interface
};

答案 1 :(得分:1)

或者你可以做最简单的事情并编写一个值类型,其复制赋值运算符抛出(和/或移动赋值运算符,复制和移动构造函数,......)。

由于您首先填充容器,因此您甚至可以根据需要选择抛出哪些值。编写迭代器的样板少得多。

NB。我假设你想通过强制异常来测试你的算法。我认为TemplateRex的建议更多地针对在运行时捕获意外误用的迭代器。随意澄清。


示例实现:

最简单的可能值类型 - 它没有任何实际值,并且总是抛出副本或移动:

struct TrivialThrowOnCopy {
  TrivialThrowOnCopy() = default;
  TrivialThrowOnCopy(TrivialThrowOnCopy const &) {
    throw std::runtime_error("copy");
  }
  TrivialThrowOnCopy(TrivialThrowOnCopy&&) {
    throw std::runtime_error("move");
  }
};

或者您可以在其中明确告诉每个实例是否抛出:

struct ConfigurableThrowOnCopy {
    bool should_throw_;
    explicit ConfigurableThrowOnCopy(bool b = false) : should_throw_(b) {}
    ConfigurableThrowOnCopy(ConfigurableThrowOnCopy const &other) 
    : should_throw_(other.should_throw_) {
        if (should_throw_) throw std::runtime_error("copy");
    }
    ConfigurableThrowOnCopy(ConfigurableThrowOnCopy &&other) 
    : should_throw_(other.should_throw_) {
        if (should_throw_) throw std::runtime_error("move");
    }
};

或每个_n_th拷贝抛出的那个:

struct CountingThrowOnCopy {
    static unsigned counter;
    // set CountingThrowOnCopy::counter = 5 to make the 5th copy throw
    CountingThrowOnCopy() = default;
    CountingThrowOnCopy(ConfigurableThrowOnCopy const &) {
        if (!--counter) throw std::runtime_error("copy");
    }
    CountingThrowOnCopy(ConfigurableThrowOnCopy&&) {
        if (!--counter) throw std::runtime_error("move");
    }
};

或上述任何内容但包含实际值:

template <typename T>
struct ConfigurableThrowOnCopyT {
    T value_;
    bool should_throw_;
    explicit ConfigurableThrowOnCopyT(T const &t, bool b = false)
    : value_(t), should_throw_(b) {}
    ConfigurableThrowOnCopyT(ConfigurableThrowOnCopyT const &other)
    : value_(other.value_), should_throw_(other.should_throw_) {
        if (should_throw_) throw std::runtime_error("copy");
    }
    ConfigurableThrowOnCopyT(ConfigurableThrowOnCopyT &&other)
   : value(std::move(other.value_)), should_throw_(other.should_throw_) {
        if (should_throw_) throw std::runtime_error("move");
    }
    T& operator() { return value_; }
    T const& operator() const { return value_; }
};

答案 2 :(得分:1)

与构造自己的迭代器不同的方法是构造已经存在的迭代器类的 decorator 类。玩具示例可能是:

#include<functional>

/**
 * @brief Decorates an iterator to permit code injection before dereferencing 
 */
template<class T>
struct IteratorDecorator : public T {

  template<class V>
  IteratorDecorator(T iterator, V f) : T(iterator) , m_callback(f) {}

  typename T::value_type & operator*() {
      m_callback();
      return T::operator*();
  }

private:
  std::function<void()> m_callback;
};

/**
 * @brief Convenience function just for type deduction 
 */
template<class T, class V>
IteratorDecorator<T> decorate(T iterator, V v) {
  return IteratorDecorator<T>(iterator,v);
}

这可以在客户端代码中使用,如下所示:

int main()
{
  vector<int> ivec {1, 3, 5, 6};

  try {
    for_each(ivec.begin(),ivec.end(),[](int& x){ cout << x << endl; } );
    for_each(decorate(ivec.begin(), [](){ cout << "decorated : "; }), 
             decorate(ivec.end()  , [](){}),
             [](int& x){ cout << x << endl; }); 
    for_each(decorate(ivec.begin(), [](){ throw runtime_error("This one throws"); }), 
             decorate(ivec.end()  , [](){}),
             [](int& x){ cout << x << endl; } );
  } catch( exception& e) {
    cout << e.what() << endl;   
  }

  return 0;
}

如果您想体验代码,可以找到工作版here