2

I need to find a way to test my exception handling in a function I wrote called hpx::parallel::copy. Other functions in the library such as hpx::parallel::transform are easy to test as a predicate can be passed in which throws an exception, but copy takes no predicate.

I think my best solution would be to use an iterator that throws on dereference somehow, though I'm not quite sure how to go about doing this......any other suggestions to solving this problem are welcome as well. Here is a code example of my problem

//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..

to be more specific, I want to be able to modify what exactly I want to throw in order to test multiple scenarios, this just means I can't use an implementation that throws out of range or other exceptions I can't control, I need to throw specific exceptions.

hkaiser
  • 11,403
  • 1
  • 30
  • 35
Syntactic Fructose
  • 18,936
  • 23
  • 91
  • 177
  • You could try writing your own iterator based on `boost::iterator_facade` (not strictly necessary, but simplifies the task a lot) and throwing in the dereference function (or any other function that you want). – anderas Jun 04 '14 at 14:54

3 Answers3

1

The easiest approach is to construct your iterators with a back-reference to the container they iterate over. Whenever you increment the container's end(), or decrement its begin(), or when you dereference anything outside the container's range, you throw an exception. Because the iterator has a reference to the container, you have all this information. The overhead is a simple reference (or pointer) per iterator, and a small amount of logic in the operator--, operator++ and operator*.

Microsoft use such an approach in their Checked Iterators which you can turn on when using their Standard Library. A sample implementation is given in SafeSTL. E.g. their vector<T> looks a bit like this:

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
};
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
1

Or you could do the simplest possible thing and write a value type whose copy assignment operator throws (and/or move assignment operator, copy and move constructors,...).

Since you populate the container in the first place, you can even select which values throw, if you want. There's a lot less boilerplate than writing an iterator.

NB. I'm assuming you want to test your algorithm by forcing an exception. I think TemplateRex's suggestion is aimed more at iterators which catch accidental misuse at runtime. Feel free to clarify.


Sample implementations:

The simplest possible value type - it doesn't have any actual value, and always throws on copy or move:

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

Or you could have one where you explicitly tell each instance whether to throw or not:

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");
    }
};

or one where every _n_th copy throws:

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");
    }
};

or any of the above but wrapping an actual value:

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_; }
};
Useless
  • 64,155
  • 6
  • 88
  • 132
1

An approach different from constructing your own iterators would be to construct a decorator class of an already existing iterator class. A toy example may be:

#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);
}

This may be used in client code like this:

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;
}

If you want to experiment with the code, you can find a working version here.

Massimiliano
  • 7,842
  • 2
  • 47
  • 62