3

I am aware that the standard declares that std::tuple is not trivially copiable so std::memcpy will have an undefined behavior on this type, but are there infos (maybe something compiler specific) to safely memcpy std::tuple with basic types (int, float)?

A possible example:

auto my_computation = [](...){ something...; return std::make_tuple(...) }

auto ret = my_computation(...);

unsigned char buf[...];
memcpy(buf, &ret, sizoof(ret));

//send the bytes out to another device

I have had no issues (for now) with this raw memcpy but I am not sure if it is reliable (g++ (Alpine 10.3.1_git20210424) 10.3.1 20210424).

I can assure that the receiver architecture is compatible with this bytes representation (e.g. endianess) so this is not a problem.

simon paul
  • 35
  • 3
  • 6
    Could you explain why you would need this? (e.g.: specific use-case) – UnholySheep Jun 07 '22 at 10:03
  • I have to transfer tuple content into a byte array (char*) for network transfer – simon paul Jun 07 '22 at 10:04
  • 1
    What about trying with some tuples and taking the addresses of all elements to check if they are contiguous ? (Note that this could be compiler-dependent.) –  Jun 07 '22 at 10:12
  • 1
    you can memcpy an `int` and a `float` seperately. On the other end you can then memcpy the `int` and the `float` into `int` and `float` members of a tuple – 463035818_is_not_an_ai Jun 07 '22 at 10:14
  • Possibly relevant: https://stackoverflow.com/q/14744303/580083 – Daniel Langr Jun 07 '22 at 10:15
  • Is it just any old tuple (of primitive types), or do you have something specific in mind? See also: https://wandbox.org/permlink/cQ2NEkQBAa4A3Paz – Paul Sanders Jun 07 '22 at 10:16
  • 1
    Does this answer your question? [Why can't std::tuple be trivially copyable?](https://stackoverflow.com/questions/38779985/why-cant-stdtupleint-be-trivially-copyable) – macomphy Jun 07 '22 at 10:16
  • 4
    Serialize it yourself by accessing the `int` and the `float` individually. IMO that's the only portable proper way to do it. – Jabberwocky Jun 07 '22 at 10:17
  • @Jabberwocky I agree. I bet `memcpy` works though (for what the OP seems to have in mind). – Paul Sanders Jun 07 '22 at 10:18
  • 1
    @PaulSanders note that "works" and "appears to work" are different things. – Aykhan Hagverdili Jun 07 '22 at 10:23
  • 6
    In the old days, there were these things named `struct`s... –  Jun 07 '22 at 10:38
  • Specifically, concerning network communication, I would've concerns to simply copy bytes. If sender and receiver have different endianess, then the better solution for integrals is bit-arithmetic to isolate the bytes. Actually, floating points have endianess as well. The only solution, I'm aware of is to memcpy the bytes of the floating point into a byte buffer and then reverse it before sending if necessary. – Scheff's Cat Jun 07 '22 at 10:40
  • 1
    gcc knows what `memcpy()` does and happily eliminates it. So I thought it would be smart enough to merge multiple calls to `memcpy()` too: https://godbolt.org/z/ceGvThEc7 Alas that is not the case. Using `memcpy()` on each member of a tuple nicely optimizes into a load and store but they aren't merged. – Goswin von Brederlow Jun 07 '22 at 10:57
  • 1
    One thing that surprised me a bit was that `std::tuple` has the float first followed by the int. Is that mandated by the standard or unspecified? – Goswin von Brederlow Jun 07 '22 at 10:58
  • 1
    @GoswinvonBrederlow This is implementation-dependent. – Evg Jun 07 '22 at 11:08
  • @GoswinvonBrederlow yes it is unspecified. Take e.g. a look [here](https://stackoverflow.com/questions/14597006/stdtuple-memory-alignment). – Jakob Stark Jun 07 '22 at 11:08
  • 4
    One more reason to define your own structure over using tuple then. – Goswin von Brederlow Jun 07 '22 at 11:23
  • In addition to memcpy'ing the int and float to your buffer, you'll also need to host-to-network swizzle the bytes. (And presumably both sides are using IEEE 754 float format, which is not required/guaranteed by the C++ standard — it's an implementation detail.) – Eljay Jun 07 '22 at 11:27
  • OK OP, [mre] please. Let's see what you're really trying to do. – Paul Sanders Jun 07 '22 at 11:29
  • 4
    `std::tuple` in this context fits to this quote: "Every new powerful feature will be overused and misused" - [Bjarne Stroustrup](https://www.stroustrup.com/quotes.html). `std::tuple` should be used in template code, when context and types of fields is unknown in all other cases `struct` is more readable and maintainable, since fields have names which are form of documentation. – Marek R Jun 07 '22 at 11:52
  • @GoswinvonBrederlow a recursive implementation of tuple does that. Probably MSVC platform. – Red.Wave Jun 07 '22 at 14:47
  • @Red.Wave gcc on godbolt – Goswin von Brederlow Jun 07 '22 at 14:49
  • @GoswinvonBrederlow recursive tuple? – Red.Wave Jun 07 '22 at 14:49
  • @Red.Wave You would have to check the libstdc++ source yourself. – Goswin von Brederlow Jun 07 '22 at 14:51

1 Answers1

3

Tuple memory representation is implementation defined, just like the order of derived class object and base class object. If your data blocks are large and individual copy creates performance issues (which is a rare and niche problem), you should create own structures with standard memory layout. This also would allow implementation of byte order change.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • 1
    A bit of a technicality: these things are implementation specific, but not implementation defined. In the C++ Standard, "implementation defined" means that a conforming implementation must document its behavior. – Pete Becker Jun 07 '22 at 12:49
  • 1
    @PeteBecker so due to the fact that this is "specific" and not "defined" there are no documentation available about the memory representation of std::tuple? – simon paul Jun 07 '22 at 12:52
  • 2
    @kmc -- there may or may not be documentation. "Implementation defined" is about what the C++ Standard requires; it means that documentation is **required**. – Pete Becker Jun 07 '22 at 12:53