2

I am using an external library which provides a function with the following interface:

void foo(const std::vector<int>& data);

I am receiving a very large C-style array from another library which has already been allocated:

int* data = bar();

Is there any way for me to pass on data to foo without allocating and copying each element? data is very large and therefore I want to avoid a copy and allocation if possible.

I could have used allocators, but foo is not templated for an allocator, so I don't believe this is possible.

I understand I may be asking for magic, but if it is possible that would be great. Of course if foo rather took an std::span this would not be a problem.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gary Allen
  • 1,218
  • 1
  • 13
  • 28
  • foo also wants an non-const reference, so it is able to modify\reallocate it. SOrry, I don't think it's possible. A library that allocates memory for large blobs is also problematic. Is it required to keep that object controlled by library at this point? – Swift - Friday Pie Jan 16 '22 at 11:16
  • You say *without* using an allocator - however, using a custom allocator to create a **local** `std:vector` that wraps your `int* data` (then passing that vector) may be a solution to avoid memory copy/moves. [This answer](https://stackoverflow.com/a/27730412/10871073) shows a way to do that. – Adrian Mole Jan 16 '22 at 11:21
  • @AdrianMole the problem is that `std::vector>` is different type? In general that looks like two incompatible library interfaces. C++ one doesn't have legacy interface (pointer + size), the C one doesn't have an interface that allows using pre-allocated storage. – Swift - Friday Pie Jan 16 '22 at 11:40
  • @Swift-FridayPie my bad, it should have been a const reference. Either way I'm also doubting whether this is possible since they are different types. I guess this is the problem that std::span solves. – Gary Allen Jan 16 '22 at 11:43

2 Answers2

1

Magic

This answer is magic, dependent on the implementation of the compiler.

We can forcibly access the container of a vector.

Take g++ as an example. It uses three protected pointers, _M_start, _M_finish, and _M_end_of_storage to handle storage. So we can create a derived class that sets/resets the pointers to the return of vaule bar() in the constructor and destructor.

Example code for g++:

static_assert(__GNUC__ == 7 && __GNUC_MINOR__ == 5 && __GNUC_PATCHLEVEL__ == 0);

class Dmy: public std::vector<int>
{
    public:
        Dmy(int *b, int *e)
        {
            _M_impl._M_start = b;
            _M_impl._M_finish = e;
            _M_impl._M_end_of_storage = _M_impl._M_finish;
        }

        ~Dmy()
        {
            _M_impl._M_start = 0;
            _M_impl._M_finish = 0;
            _M_impl._M_end_of_storage = 0;
        }
};

foo(Dmy(data, end_of_data));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
yao99
  • 870
  • 5
  • 12
  • This is great, thank you! I understand it is compiler type and version dependent, but in extreme cases with millions of integers in very tight code, it will be worth it. And if it works, it works (just have to check up on it when changing compiler versions). Appreciate the magic! – Gary Allen Jan 17 '22 at 12:08
0

Is there any way for me to pass on data to foo without allocating and copying each element?

No, there's no way of avoiding it given the premise.

There are two alternative workarounds by changing the premise:

  • Change the other library to return a vector.
  • Change foo to not require a vector. As you point out, std::span would probably be a reasonable choice.
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Thanks for the reply, but unfortunately both library's APIs are out of my control, as implied when I mentioned that foo is from an "external library". I appreciate the help though! I guess this is one of the major downfalls of C/C++ interoperability. – Gary Allen Jan 16 '22 at 13:14
  • @GaryAllen `I guess this is one of the major downfalls of C/C++ interoperability.` I wouldn't say so. It's rather a downfall of the design of the API that you're using. – eerorika Jan 16 '22 at 13:31
  • mmm not sure I disagree. Any C API uses raw points like that. Majority of C++ APIs pre C++20 use std::vector instead of std::span. If I require the interoperability of these two libraries, I would surely not blame their API for incompatibility. Anyways – Gary Allen Jan 16 '22 at 13:36
  • @GaryAllen `Majority of C++ APIs pre C++20 use std::vector instead of std::span` Spans existed way before they were added to the standard library in C++20. Using `std::vector` when you don't need it is a bad design (from efficiency perspective). – eerorika Jan 16 '22 at 13:43
  • theoretically I'd agree, but the library in question is OpenCV, probably the most widely used computer vision API. Technically, I'd agree that it's a bad design, but when plenty of popular C++ libraries do it, its hard to justify that its their fault – Gary Allen Jan 16 '22 at 14:03
  • @GaryAllen Poor API is the fault of whoever designed the API; that's easy to justify. "Others write inefficient APIs too" is a poor excuse. – eerorika Jan 16 '22 at 14:19