0

I have a class whose purpose is to move data which might have alignment restrictions to or from a serialized memory buffer where the data is not aligned. I have set and get handlers as follows:

#include <cstring>
template<typename T>
struct handle_type {    
    static inline T get(const void *data) {
        T value;
        memcpy(&value, data, sizeof(T));
        return value;
    }
    static inline void set(void *data, T value) {
        memcpy(data, &value, sizeof(T));
    }
};

After c++20 comes out, I am hoping it will become something like the following:

#include <bit>
template<typename T>
struct handle_type {    
    union unaligned { char bytes[sizeof(T)];  }; // EDIT: changed from struct
    static inline T get(const void *data) {
        return std::bit_cast<T>(*(const unaligned*)data);
    }
    static inline void set(void *data, T value) {
        *(unaligned *)data = std::bit_cast<unaligned>(value);
    }
};

Will it work? Or is the fact that I am using an inerim type, the unaligned struct type, liable to pose a problem?

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
markt1964
  • 2,638
  • 2
  • 22
  • 54

1 Answers1

4

Your get function is UB unless the user has provided a pointer to an unaligned object. Your set function is similarly UB unless data is an unaligned object. Either of these cases violates strict aliasing. Remember: the strict aliasing back-door is about an actual char*; that's not the same as an object that happens to contain a char*.

Also, set is potentially a compile error, depending entirely on whether or not the implementation of unaligned is such that it has the same size as T. After all, unaligned may have padding at the end.

bit_cast wants to deal primarily in objects, not random buffers of memory. For that, you should stick with memcpy.


<changed to use union instead of struct>

That changes nothing; using a union doesn't guarantee that the size of the union is equal to the size of its largest data member. From [class.union]/2:

The size of a union is sufficient to contain the largest of its non-static data members.

Emphasis added: "sufficient", not "equal to". The standard permits implementations the ability to make the union larger than its largest data member, as such a size would still be "sufficient".

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • It's my undertsanding that a struct that *only* contains elements of type X has the same alignment criteria as X, and whose size = sizeof(X) * the number of elements of X in the struct. – markt1964 Nov 22 '19 at 01:46
  • @markt1964: Show me in the standard where it says that. I can show you [the part of the standard that says](https://timsong-cpp.github.io/cppwp/n4659/class.mem#24), if the type is standard layout, byte offset to the first member is 0. I can show you [the part that says](https://timsong-cpp.github.io/cppwp/n4659/class.mem#17) that members of the same access control are allocated such that later have larger offsets than earlier. But I see no explicit prohibition in the standard from the implementation inserting padding at the end of a type that only has one member. – Nicol Bolas Nov 22 '19 at 02:19
  • 3.9.2-4 basic.types. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2342.htm#3.9/2 – markt1964 Nov 22 '19 at 02:37
  • @markt1964: That defines trivial copyability, not *size*. `bit_cast` requires that the two objects have the same size. And FYI, in the actual standard, [that lives here](https://timsong-cpp.github.io/cppwp/n4659/basic#types-2). – Nicol Bolas Nov 22 '19 at 03:35
  • Which they do. See 3.9.4. Further they would not be copyable in such a way if they were not the same size. – markt1964 Nov 22 '19 at 03:44
  • @markt1964: I don't see the part that says anything about the size of the object. It only says that there's a distinction between the object representation (all the bits) and the value representation (possibly less than all the bits). Nowhere does it say that trivial types shall have a size equal to the size of its members. – Nicol Bolas Nov 22 '19 at 03:48
  • @markt1964: "*they would not be copyable in such a way if they were not the same size*" Who says that they (we're still talking about the case of a struct of one member and an object of that member's type, right?) would have to be copyable? Two types which happen to be trivially copyable are not trivially copyable between each other. Even `bit_cast` has a requirement that the object data of the source must generate a valid value of the destination type. – Nicol Bolas Nov 22 '19 at 03:51
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/202908/discussion-between-markt1964-and-nicol-bolas). – markt1964 Nov 22 '19 at 16:21