I ran into the following efficiency problem when writing a C++ wrapper around a C library that uses raw buffers for communication (the library in this case is MPI):
std::vector<T> wrapper_function() {
std::size_t size = native_function_get_size();
...; // allocate memory
T* data = ...; // get pointer to memory
native_function(data, size, ...)
... // return vector<T>(data);
}
Constraints on the problem: the C++ library user expects an interface using std::vector, while the C library exposes a "unique_ptr" like interface using the pair (T*, size).
IMHO:
- this problem is common and relevant when writing C++ wrappers around C libraries (see example above),
- it is desirable to provide thin, safe, and efficient wrapper around C libraries, and
- there is currently no good solution available.
To the authors best knowledge, the most efficient solution is:
std::vector<T> wrapper_function() {
std::size_t size = native_function_get_size();
std::vector<T> data_vector (size); // overhead: vector default constructs elements!
native_function(data_vector.data(), size, ...)
return data_vector;
}
It is worth to remark that any solution considered should play well with allocators.
Proposed solution: provide a constructor and a resize methods that do not perform initialization.
Bikeshed 1:
struct uninitialized_t {};
const constexpr uninitialized_t uninitialized {};
std::vector<T>(size, std::uninitialized)
std::vector<T>::resize(size, std::uninitialized)
The problem could then be solved as:
std::vector<T> wrapper_function() {
std::size_t size = native_function_get_size();
std::vector<T> data_vector (size, std::uninitialized);
native_function(data_vector.data(), size, ...)
return data_vector;
}
Bikeshed 2:
std::vector<T>::uninitialized_resize(size)
The problem could then be solved as:
std::vector<T> wrapper_function() {
std::size_t size = native_function_get_size();
std::vector<T> data_vector();
data_vector.uninitialized_resize(size);
native_function(data_vector.data(), size, ...)
return data_vector;
}
Anyhow, both alternatives allow developing efficient C++ wrappers of C libraries that maintain a native C++ interface, and discourage used of uninitialized memory by providing a verbose interface.