6

I am working with an external library (pcl) so I need a solution that does not change the existing function prototypes.

One function that I am using generates an std::vector<int, Eigen::aligned_allocator<int>>. The function that I want to call next expects a const boost::shared_ptr<std::vector<int, std::allocator<int>>>. I do not want to copy the elements because it is in an already slow critical part of my code. If not for the allocator mismatch, I would get around the shared_ptr requirement by simply doing:

// code that generates std::vector<int, Eigen::aligned_allocator<int>> source
boost::shared_ptr<std::vector<int>> indices(new std::vector<int>);
indices->swap(source);
// use indices as intended

This does not compile with the MSVC compiler because it cannot convert between these two vector types. The only solution I have thought of so far that does not copy the contents is:

// code that generates std::vector<int, Eigen::aligned_allocator<int>> source
boost::shared_ptr<std::vector<int>> indices(new std::vector<int>);
indices->swap(reinterpret_cast<std::vector<int>&>(source));
// use indices as intended
indices->swap(reinterpret_cast<std::vector<int>&>(pcout.points));

Note how I need to use indices as as a const shared_ptr. I believe that the allocator will not play a role in the swap operation. The ints should also not need any padding to be aligned as they are already of size 32-bit. The std::allocator version should be able to read from the aligned version because it can only allocate in memory addresses that the std::allocator could have used anyway. Finally, I swap back because the aligned allocator might crash if it tries to delete a non-aligned reserved space.

I tried it and it didn't crash but that is not enought to convince me that it is actually correct. Is it safe? If not, is it conditionally safe if certain reasonable assumptions are made concerning the compiler? Is there a safer alternative that does not noticeably impact performance?

Please do not reply with "profile your code", "not worth it" or similar answers. Even if they are applicable here, there is theoretically a scenario where copying is not a viable solution, and this thread should address that.

Similar question is talking about copying the data in a clean way as was clarified in the comments.

EDIT: it seems that despite Eigen::aligned_allocator being designed for 16-bit alignment, no extra padding is added to the ints. Comparing the address of the first and last element on the list gives the size one would expect from the number of elements and the sizeof(int). This means that the ints are stored in a way that should be compatible with the std::allocator version. I hope I'll have time later today or in the coming days to make a more complete test.

patatahooligan
  • 3,111
  • 1
  • 18
  • 27
  • 3
    Possible duplicate of [Converting between vectors with different allocators](https://stackoverflow.com/questions/8190889/converting-between-vectors-with-different-allocators) – Passer By Jul 06 '17 at 15:34
  • It seems that Eigen aligns at 16bytes, which is more than I initially thought. If I understand how std::vector uses allocators, this means that ints have padding in between and the reinterpret_cast probably causes indices to interpret the garbage data in between as valid ints. – patatahooligan Jul 06 '17 at 15:35
  • If you hope for a standard conformant answer, you are out of luck apparently – Passer By Jul 06 '17 at 15:35
  • @PasserBy I opened this question because the other had answers that involved copying which is what I'm hoping to avoid. Also, as I said I would like to know if this works for common compilers even if is not standard compliant – patatahooligan Jul 06 '17 at 15:37
  • 1
    PCL being an open source library, I would consider patching it a possibility (if feasible). – Dan Mašek Jul 06 '17 at 15:39
  • Of course that is possible and I might do it because the interface for the module I'm using is pretty outdated. However, I find this an interesting technical question and one that might not always have such a workaround so I would still like to see if anyone comes up with something clean and efficient.. – patatahooligan Jul 06 '17 at 15:43
  • @patatahooligan Its interesting in a way that shows how broken having different allocators in the type is. This has been discussed at length at many many places. The other _question_ is about how to do what you propose to do, but the answer says: no its not possible without a copy – Passer By Jul 06 '17 at 15:48
  • 1
    @patatahooligan The correct answer here is creating a "view" on containers and pass that around instead of const references. But your function signature will still change – Passer By Jul 06 '17 at 15:53

1 Answers1

4

If you have the ability to change the function prototype to another vector type, then there is an entirely new namespace (namespace pmr) in the standard library that uses type erasure for allocators to ensure compatibility between containers that use different allocators.

For more see polymorphic_allocator: when and why should I use it?

With that change you can simply do

void foo(std::pmr::vector<int>& vec); 

and pass in a vector type with any allocator you want (as long as it is also a std::pmr::vector) you want.

If you are not able to change the type of vector the function expects than I don't think you can do much better than copying/moving the elements one by one.

reinterpret_casting two different vector instances to different types and then using methods on them is very dangerous.

Curious
  • 20,870
  • 8
  • 61
  • 146
  • 1
    I am hoping for a cleaner solution that changing the function prototypes, but this is a feature that I was not aware of. – patatahooligan Jul 06 '17 at 15:48
  • @patatahooligan I am afraid there is no better solution. There is no good way to get to vector types that have different allocators to cooperate with each other – Curious Jul 06 '17 at 16:14