11

During active development of a class that uses std::vector, it often happens that an index is out of bounds. (See this code review question for a practical example.) When using operator[], this results in undefined behavior. Still, the [] syntax is easy to read an more convenient than writing .at().

Therefore I'd like to write my code using the [] operator, but at the same time have bounds checks enabled. After testing the code, it should be very easy to remove the bounds checks.

I'm thinking of the following code:

util::bound_checked<std::vector<int>> numbers;

numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
numbers.push_back(4);
std::cout << numbers[17] << "\n";

To me, this utility template seems to be so straight-forward that I'd expect it to exist. Does it? If so, under which name?

Roland Illig
  • 40,703
  • 10
  • 88
  • 121
  • Some compilers like to prefer `at()` as the default implementation of the `operator[]` with `_DEUG` enabled. – user0042 Dec 30 '17 at 15:45
  • Which compiler? – HolyBlackCat Dec 30 '17 at 15:55
  • 3
    You have a problem with your design, not with bounds checking. A vector knows it’s size, magic numbers are evil. –  Dec 30 '17 at 15:59
  • `operator[]` and `at` have different semantics. The choice is not one of convenience or verbosity. `at` throws an exception, and exceptions are useful if other code handles them in a `catch` block. How exactly would you "handle" an out-of-bounds error at such a low level of abstraction other than by terminating the current process? In fact, `at` should be considered a design error of `std::vector`. Don't use it, ever. The second thing is: Why do you want to remove bounds checking in production? Ship what you've tested, please. – Christian Hackl Dec 30 '17 at 20:28

4 Answers4

7

I don't think anything like this exists, but it's fairly easy to create:

template <class Container>
struct bound_checked : public Container
{
  using Container::Container;

  auto operator[] (typename Container::size_type i) -> decltype(this->at(i))
  { return this->at(i); }

  auto operator[] (typename Container::size_type i) const -> decltype(this->at(i))
  { return this->at(i); }
};

[Live example]

Note that the above actually uses a discouraged practice, namely public inheritance from standard containers (which are not designed for it). My understanding of your question was that this wrapper would be used in testing purposes only, which is fine. However, if you want this to remain in production and want to be on the very safe side, use non-public inheritance:

template <class Container>
struct bound_checked : private Container
{
  using Container::Container;

  auto operator[] (typename Container::size_type i) -> decltype(this->at(i))
  { return this->at(i); }

  auto operator[] (typename Container::size_type i) const -> decltype(this->at(i))
  { return this->at(i); }

  using Container::begin;
  using Container::end;
  using Container::at;
  using Container::insert;
  // ... you get the idea
};
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 1
    From [this post](https://stackoverflow.com/questions/16660437/vector-and-operator-overloading) it seems to be not recommended to do this. – super Dec 30 '17 at 15:53
  • 2
    @super Yes, `std` containers are not intended for public inheritance. If you prefer, you can inherit privately and fill the class with `using` declarations. My impression was that this was supposed to be for testing only and usage of this class removed from final code. I will however add a disclaimer to the answer. – Angew is no longer proud of SO Dec 30 '17 at 15:55
4

If you use GCC (possibly MinGW), or Clang with libstdc++ (GCC's standard library), then #define _GLIBCXX_DEBUG will do what you want.

Or even better, define it with a flag: -D_GLIBCXX_DEBUG.

Alternatively, there's also _GLIBCXX_ASSERTIONS, which performs less checks, but compiles (and presumably runs) faster.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • 1
    MSVC automatically turns on this type of behavior in debug builds. – SoronelHaetir Dec 30 '17 at 16:52
  • @SoronelHaetir: MSVC does that if you specify a debug runtime library, e.g. with `/MTd`. There's nothing automatic about it, unless you mean that the Visual Studio IDE automatically adds this compiler option in a new project's "Debug" configuration. – Christian Hackl Dec 30 '17 at 20:29
4

To me, this utility template seems to be so straight-forward that I'd expect it to exist

For gcc it does exist. gcc libstdc++ has a set of debug containers. For std::vector it has __gnu_debug::vector debug container. See documentation.

ks1322
  • 33,961
  • 14
  • 109
  • 164
1

Does it? If so, under which name?

I'm pretty sure it does exist. Many compilers enable std::vector<T>::at() as implementation for std::vector::operator[]() if a compiler intrinsic __DEBUG building macro is enabled.

Therefore I'd like to write my code using the [] operator, but at the same time have bounds checks enabled. After testing the code, it should be very easy to remove the bounds checks.

Regarding the naming, I'd say that's debug vs. release building. The checks will be removed when the code is compiled without the __DEBUG definition.

user0042
  • 7,917
  • 3
  • 24
  • 39
  • 1
    Can you name a single compiler that actually does that? I very much doubt it. It would mean that instead of crashing and/or producing a core dump, the next `catch` handler would be able to catch the resulting exception and continue execution of the program. – Christian Hackl Dec 30 '17 at 20:23
  • This answer would be good if it were backed up by some references to actual documentation. "a compiler intrinsic" sounds more like guessing than like knowledge. – Roland Illig Jan 06 '18 at 15:18