2

I am trying to pass an object that contains a boost::any data member through a networking API to exchange data between two applications. I know that the API uses memcpy internally to copy the data, but I'm not sure if what I am trying to do is invoking undefined behavior.

I wrote up a simple example to demonstrate using memcpy in this way:

#include <boost/any.hpp>
#include <cstring>
#include <string>
#include <iostream>

class Data
{
public:
    template <typename T>
    Data(T value)
        : m_value(value)
    {}

    template <typename T>
    T Read() const
    {
        return boost::any_cast<T>(m_value);
    }

private:
    boost::any m_value;
};

int main()
{
    Data src(std::string("This is some data."));
    std::cout << "Size: " << sizeof(src) << std::endl;
    std::cout << "SRC: " << src.Read<std::string>() << std::endl;

    void* dst = malloc(sizeof(src));
    std::memcpy(dst, &src, sizeof(src));

    const auto data = static_cast<Data*>(dst);
    std::cout << "DST: " << data->Read<std::string>() << std::endl;

    std::free(dst);
}

This code appears to work, and prints the following output:

Size: 8
SRC: This is some data.
DST: This is some data.

But depending on the type stored in the Data object wouldn't the size change? No matter what type I use it always prints that the size is 8.

Is this code invoking undefined behavior? If it is, how can I fix it so I can properly memcpy an object that contains a boost::any data member?

tjwrona1992
  • 8,614
  • 8
  • 35
  • 98
  • `sizeof ( boost::any)` is a constant. It is always the same, no matter what type it holds. – 463035818_is_not_an_ai Oct 08 '21 at 17:33
  • 3
    Generally you should *never* use `std::memcpy` to copy any C++ object. It will do a byte-wise straight copy, and won't invoke copy-constructors which means complex objects might not be copied in a valid way. – Some programmer dude Oct 08 '21 at 17:33
  • related: https://stackoverflow.com/questions/49730735/get-the-size-of-stdany – 463035818_is_not_an_ai Oct 08 '21 at 17:34
  • @Someprogrammerdude I don't have a choice in this case because the `memcpy` is called internally inside the networking API. I just need to make sure the type I pass to it can validly be copied using `memcpy` – tjwrona1992 Oct 08 '21 at 17:35
  • @463035818_is_not_a_number does this mean that this is well defined and I can do it this way? – tjwrona1992 Oct 08 '21 at 17:36
  • 6
    this is also related: https://en.cppreference.com/w/cpp/types/is_trivially_copyable. If it isnt `is_trivially_copyable` then you cannot just memcpy it, because copying it needs more than copying bits and bytes. `any` certainly isnt. – 463035818_is_not_an_ai Oct 08 '21 at 17:36

1 Answers1

8

any contains a pointer, and has a destructor, and is overall something you don't want to memcpy. It's working here because both src and dst are in the same memory space and because you’re freeing the object without running the destructor.

It's potentially okay to memcpy the pointed-to object held by the any (the object returned by any_cast). It is definitely not okay to memcpy the any itself, or an object containing it.

Sneftel
  • 40,271
  • 12
  • 71
  • 104
  • 1
    No double-free because the second object is not a Data, but a raw pointer to Data that is freed via `std:free` which doesn't run destructors. OP is lucky that a memcpy'd `std::string` "sort of works if the original hasn't been destroyed and you don't try anything funny with it." Would definitely not work cross-process. – Raymond Chen Oct 08 '21 at 17:39
  • Good catch, I saw `free` and thought `delete`. – Sneftel Oct 08 '21 at 17:55