0

Background / Setup

For a project I am working on, I have a class which describes a message that is sent/recieved by a client/server. Getting the proper members initialized was a big hurdle for me, but this stackoverflow question gave me a lot of insight on how to implement the basic functionality.

below is a somewhat simplified version of my code right now (I ommitted extraneous information which doesn't pertain to the question):

class Message
{
public:
    enum class tags_t {T1, T2, T3};

    // initializes a message and union member associated with T1
    Message(tags_t tag);
    Message() : Message(tags_t::T1) {};

    class T1
    { // non-trivial member data };
    class T2
    { // non-trivial member data };
    class T3
    { // non-trivial member data };

    void set_data(const T1& d);
    // all members ...

    void get_data(T1& d);
    // all members ...


    // destructs old type, constructs new type
    Message& operator=(const Message& other);

private:
    tags_t m_tag;

    union
    {
        T1 u_t1;
        T2 u_t2;
        T3 u_t3;
    };
};

My Goal

I want to add a member to access (and limit access) the union member data. (ie, only access a union member if it has been initialized). When I use my code, I want the usage to look something like this:

Set

Message msg(Message::tags_t::T1); // initialize message
message.data = { /* T1 */ };      // assign message data

Get

Message::T1 t1;
Message msg();
socket.recieve(msg);                    // this could be T1, T2, or T3, but T1 is expected
t1 = msg.data; // or: t1 = msg.data();  // since msg knows what type it is, it should be able to
                                        //  assign t1 if m_tag == tags_t::T1

essentially, I want some fancy c++ way to get/set the message data. I thought about adding a member called data in Message, which is a subclass with its assignment operators overloaded, like this:

class Message
{
    ...
    class D
    {
        // some operator overloads
        // and c++ magic
    } data;
    ...
};

but I was unable to arrive at a solution.

TLDR: I feel like there should be a way to create a accessor member of Message which will automatically get/set the union members based on the type of the other operand of the assignment operator, provided that their types match.

I hope I was able to articulate my question sufficiently, just let me know if you need me to explain anything in more detail. Thanks in advance for taking time to help me out!

Ethan Smith
  • 67
  • 1
  • 7
  • 3
    Don't use `union`, use `std::variant`. – EOF May 20 '21 at 14:32
  • @EOF from cppreference: "As with unions, if a variant holds a value of some object type T, the object representation of T is allocated directly within the object representation of the variant itself. Variant is not allowed to allocate additional (dynamic) memory." since my types allocate additional memory, this gave me the impression that std::variant would not be useful for me. If this is not the case, please let me know! I like simple solutions. – Ethan Smith May 20 '21 at 14:40
  • 2
    @EthanSmith It means the actual object exists within the storage of the `std::variant` exactly the same way `union` works. It means `std::variant` holding a `T` can't be implemented as a `unique_ptr`. It doesn't mean you can't put an objects that internally manage external memory in a `variant`. The sentence you quoted starts with "As with unions"` because `std::variant` fundamentally works the same way as `union`s. – François Andrieux May 20 '21 at 14:45
  • I personally would at first only receive the number of bytes needed for the tag, determine the message type and set it appropriately within `Msg` struct, finally read into the data object. You might need appropriate serialisation anyway, just transferring the whole struct as is might lead to portability problems. – Aconcagua May 22 '21 at 07:04

0 Answers0