3
votes

I know that g++ (and MSVC) have switches that allow bounds checking on operator[] , unfortunately, to my knowledge, LLVM's libc++ doesn't have a complete implementation of such switches or debug code.

On my current project I have been using my own implementation of vector (that I wrote for portability a few years back) which doesn't throw exceptions and has assert based bounds checking on operator[] and at (in fact one calls the other and they behave identically as there are no exceptions).

I'm going to be handing over this code-base after I've finished my current program and it may be in use for a long time. Since I'm not supposed to be required to maintain it or anything I would rather be fully standard compliant everywhere and I don't feel that re-implementing a container is in the spirit of the standard, (I also highly doubt that my container is as good as one written by the libc++ or libstdc++ team).

Is there some preprocessor magic or similar that I can do to make operator[] behave like at() during debug (so it aborts due to an uncaught exception) and behave like operator[] when I disable this debug mode? (Project is fully C++14 supported)

2
You wrote a non-standard reinvention of a standard container for ... portability? - Lightness Races in Orbit
assert(i < vector.size()); auto x = vector[i]; /* etc */. - Rapptz
@LightnessRacesinOrbit: one quite famous C++ author/speaker I used to work with locked himself and his team away for a couple years writing their own containers claiming the commercial compiler versions were too buggy and varied - by the time he finished (about 10 years ago), all the major compiler vendors had good libraries and nobody wanted to use his. Still - such things had their day (somewhat earlier than that).... - Tony Delroy
One half-way reasonable option is to change "your vector" so it wraps a std::vector - using its implementation - while customising operator[] to call the vector's at() in debug builds. That still means having a name other than std::vector hanging around, which isn't ideal.... - Tony Delroy
@LightnessRacesinOrbit It wasn't for portability per se, we had a Variant type and needed sizeof(Vector) to be the same on all platforms, that requirement isn't in the project, but we continued to use our containers until I realised that long-term that could be a bad idea. - Goobley

2 Answers

7
votes

We can see from the trunk libc++ source code for vector that there is indeed such a check inside std::vector<>::operator[], which goes through _LIBCPP_ASSERT.

Unfortunately, the libc++ debug feature is not yet functional.

So:

  • Watch for libc++ updates
  • Train your team to expect operator[] to silently accept broken input. That's what it does, by definition. Coming to rely on implementation-specific additional sanity checks is a really bad idea. Your team should be writing their own asserts if they're not sure what they're doing.
-1
votes

Is there some preprocessor magic or similar that I can do to make operator[] behave like at() during debug (so it aborts due to an uncaught exception) and behave like operator[] when I disable this debug mode? (Project is fully C++14 supported)

Uhhh ... how about this?

const T& your_vector::operator[](std::size_t index) const
{
#ifndef NDEBUG // !! double negative condition
    if(index >= size_) // assume size_ is std::size_t size_; member of class
        throw std::out_of_bounds{"const T& your_vector::operator[]"};
#endif
    return data_[index]; // assume data_ is a native array containing the elements
}

const T& your_vector::at(std::size_t index) const
{
    if(index >= size_) // assume size_ is std::size_t size_; member of class
        throw std::out_of_bounds{"const T& your_vector::at"};
    return data_[index]; // assume data_ is a native array containing the elements
}

The indexing operator implementation is identical to at when DEBUG is defined and faster when the macro is not defined.