Repository: opencv
Issue: `_InputArray` and its derivatives need a clean-up
Labels: bug,category: core,priority: high,RFC

### System Information

All systems.

### Detailed description

`_InputArray` and its derivatives have different behaviour based on what kind of "container" they hold. The code makes many mistakes about how C++'s memory model works, which ultimately leads to tcmalloc crashes when testing a `std::vector` optimisation that I've been working on for libc++.

For example, the following snippet simultaneously treats `obj` as `std::vector<uchar>*` and `std::vector<Vec2b>*` when `esz == 2`, which is impossible: `obj` can only be one of these types at a time.

```cpp
CV_Assert(!fixedSize() || len == ((std::vector<uchar>*)v)->size() / esz);
switch( esz )
{
case 1:
  ((std::vector<uchar>*)v)->resize(len);
  break;
case 2:
  ((std::vector<Vec2b>*)v)->resize(len);
  break;
```

Further, `_InputArray` can hold vectors of vectors of many types, but this section only seems to handle `vector<vector<uchar>>`.
-----
The crashes that I see are because OpenCV is erasing `vector<T>` to `vector<uchar>`. Up until now, `std::vector` has been implemented using three pointers (one that points to the beginning of the buffer, one that points to where the next element should be inserted, and one that acts as a sentinel for reallocation). This implementation stores where the allocated memory is starts and ends, allowing OpenCV's type punning to accidentally work.

I'm working on a patch to `std::vector` that redesigns it to use one pointer and two integers (one for the size and capacity, respectively). This means that the end boundary needs to be computed by `std::vector` during deallocation and since we're working `uchar`---which is smaller than the original type---the boundary calculation is substantially smaller than it should be. tcmalloc notices this, and forces a crash.

@zygoloid suggested having `_InputArray` store a pointer to a global object that dispatches to the member functions that implement the behaviour with the correct types. E.g.
```cpp
struct _InputArrayOps {
  virtual Mat getMat_(void* data, int idx) = 0;
  // ... other operations ...
};

// _InputArray operations for const std::vector<T>&
template<typename T> struct _InputArrayOps_vector : _InputArrayOps {
  Mat getMat_(void* data, int idx) override {
    CV_Assert( idx < 0 );
    std::vector<T>& v = *static_cast<std::vector<T>*>(data);
    return !v.empty() ? Mat(v.size(), traits::Type<T>::value + ACCESS_READ, (void*)v.data()) : Mat();
  }
  // ... other operations ...
};
template<typename T> constexpr inline _InputArrayOps_vector<T> _InputArrayOps_vector_instance;

// ... other supported types ...

class _InputArray {
public:
  // ...
  template<typename T> _InputArray(const std::vector<_Tp>& vec)
    : data(&vec), ops(&_InputArrayOps_vector_instance<T>) {}
  // ...
  Mat getMat_(int idx = -1) const { return ops->getMat_(data, idx); }
  // ...
private:
  void *data;
  _InputArrayOps *ops;
};
```

I'm working on a prototype to see how this works with my new vector representation.

### Steps to reproduce

I can't provide steps to repro just yet, but will work with you until we can get something.

### Issue submission checklist

- [X] I report the issue, it's not a question
- [X] I checked the problem with documentation, FAQ, open issues, forum.opencv.org, Stack Overflow, etc and have not found any solution
- [X] I updated to the latest OpenCV version and the issue is still there
- [ ] There is reproducer code and related data files (videos, images, onnx, etc)