Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile time triggered range check for std::vector

Tags:

c++

c++11

vector

The goal:

I would like to have a range checked version of std::vector's operator [] for my debug builds and no range check in release mode.

The range check in debug mode is obviously good for debugging, but it causes a slowdown of 5% - 10% in my release code which I would like to avoid.

Possible solutions:

I found a solution in Stroustrup's "The C++ programming language". He did the following:

template <class T>
class checked_vector : public std::vector<T> {
    public:
        using std::vector<T>::vector;

        //override operator [] with at()
};

This is problematic because it inherits from a class with non-virtual destructor which is dangerous. (And the Lounge was not too fond of that solution.)

Another idea would be a class like this:

template <class T>
class checked_vector {
    std::vector<T> data_;

    public:
        //put all public methods of std::vector here by hand

};

This would be both tedious and create a large amount of copy-paste which is bad too.

The nice thing about both the above solutions is that I can simply toggle them on and off with a macro definition in my makefile.

The questions:

  1. Is there a better solution? (If not, why not?)
  2. If not, is one of the above considered acceptable? (I know this one is opinion based, please focus on No. 1 if possible.)
like image 805
Baum mit Augen Avatar asked Jun 16 '14 14:06

Baum mit Augen


2 Answers

If I'm not mistaken, this is the usual situation with Visual Studio. With g++, you have to invoke the compiler with -D_GLIBCXX_CONCEPT_CHECKS -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC. (It's probable that you don't need all three, but I use all three systematically.) With other compilers, check the documentation. The purpose of the undefined behavior in the standard here is precisely to allow this sort of thing.

like image 136
James Kanze Avatar answered Oct 13 '22 08:10

James Kanze


In decreasing order of preference:

  • If you utilize iterators, ranges, and iteration rather than indexes into the container, the problem just goes away because you are no longer passing in arbitrary indexes that need to be checked. At that point you may decide to replace any remaining code that requires indexes with at instead of using a special container.

  • Extend with algorithms rather than inheritance as suggested in one of the comments. This will almost certainly be completely inlined away and is consistent with the standard using algorithms rather than additional member functions. It also has the advantage of working with any container that has operator[] and at (so it would also work on deque):

    template <typename Container>
    const typename Container::value_type& element_at(const Container& c, int index)
    {
        // Do checked code here.
    }
    
  • Privately inherit std::vector and using the methods you need into your child class. Then at least you can't improperly polomorphically destroy your child vector.

like image 4
Mark B Avatar answered Oct 13 '22 08:10

Mark B