Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ equivalent of Rust enums

Tags:

c++

enums

This example shows an elegant way to deal with messages of different types in Rust. It has 4 variants and some variants have sub members that are only accessible if the enum is of that specific type. A similar pattern is also possible in TypeScript.

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

In C++ this would most likely compare to the following code.

struct Message
{
    enum MessageType {QUIT, MOVE, WRITE, CHANGECOLOR} type;
    union MessageContent
    {
        struct Move { int x; int y;} move;
        std::string write;
        std::tuple<int, int, int> changeColor;
    } content;
};

However, this way isn't typesafe and memory management is going to become messy (e.g. making sure that string gets released if Message gets destructed if MessageType is WRITE). What is the best way to do this in modern C++?

like image 357
Kingsly2407 Avatar asked Sep 22 '20 21:09

Kingsly2407


People also ask

Are there enums in C?

Enumeration or Enum in C is a special kind of data type defined by the user. It consists of constant integrals or integers that are given names by a user. The use of enum in C to name the integer values makes the entire program easy to learn, understand, and maintain by the same or even different programmer.

What is #[ repr C )]?

#[repr(C)] Structs. The alignment of the struct is the alignment of the most-aligned field in it. The size and offset of fields is determined by the following algorithm. Start with a current offset of 0 bytes. For each field in declaration order in the struct, first determine the size and alignment of the field.

How are enums stored in C?

Enumeration "values" aren't stored at all, as they are compile-time named constants. The compiler simply exchanges the use of an enumeration symbol, with the value of it. Also, the type of an enumeration value is of type int , so the exact size can differ.

What size are enums in C?

The C standard specifies that enums are integers, but it does not specify the size. Once again, that is up to the people who write the compiler. On an 8-bit processor, enums can be 16-bits wide. On a 32-bit processor they can be 32-bits wide or more or less.


1 Answers

This is what std::variant is for.

(Though note, since C++ does not have pattern matching, using variants is still rather cumbersome compared to languages like Rust.)

Using std::variant, your example would look like this:

struct Quit {};
struct Move { int32_t x; int32_t y; };
struct Write { std::string s; };
struct ChangeColor { int32_t r; int32_t g; int32_t b; };
using Message = std::variant<Quit, Move, Write, ChangeColor>;

The closest thing to Rust's match expressions is std::visit. Visiting this example variant could look like:

// Utility to allow overloading lambdas for use in std::visit
template<class... Ts>
struct overload : Ts... {
    using Ts::operator()...;
};
template<class... Ts>
overload(Ts...) -> overload<Ts...>;

int main() {
    auto visitor = overload{
        [](const Quit& q)        { std::cout << "Quit\n"; },
        [](const Move& m)        { std::cout << "Move " << m.x << " " << m.y << "\n"; },
        [](const Write& w)       { std::cout << "Write " << w.s << "\n"; },
        [](const ChangeColor& c) { std::cout << "ChangeColor " << c.r << " " << c.g << " " << c.b << "\n"; }
    };

    Message m1{Quit{}};
    Message m2{Move{1, 2}};
    Message m3{Write{"a"}};
    Message m4{ChangeColor{1, 2, 3}};
    std::visit(visitor, m1);
    std::visit(visitor, m2);
    std::visit(visitor, m3);
    std::visit(visitor, m4);
}
// This prints:
//   Quit
//   Move 1 2
//   Write a
//   ChangeColor 1 2 3
like image 110
0x5453 Avatar answered Sep 28 '22 19:09

0x5453