Skip to content

Code-gen for C++ #2647

@emilk

Description

@emilk

To generate the C++ object hierarchy there are a few things to figure out.

One of them is .hpp vs .cpp files. This is probably quite easy.

Sum types

How do we express sum types in C++?

Consider a simple example (in Rust):

pub enum Rotation3D {
    Quaternion(Quaternion),
    AxisAngle(RotationAxisAngle),
}

Inheritance

Using inheritance for sum-types adds a lot of allocation and indirections, which will be quite costly.

std::variant

std::variant was introduced in C++17, and tries to be like a Rust union. Some downsides:

  • Minimum supported version of C++17
  • It uses the the type-id of the thing it holds, i.e. cannot distinguish between float degrees and float radians unless degrees and radians are their own new-types
    *std::variant is also overall very unergonomic, so we would still want to generate wrappers around it
  • It reportedly compiles quite slowly (so much template magic)

Tagged unions

For Plain Old Data (POD) a tagged union is very efficient:

enum Rotation3DKind {
    Quaternion,
    AxisAngle,
};

union Rotation3DData {
    Quaternion quaternion;
    AxisAngle axis_angle;
};

struct Rotation3D {
    kind: Rotation3DKind,
    data: Rotation3DData,

    // implicit conversions:
    Rotation3D(Quaternion);
    Rotation3D(AxisAngle);
};

However, this becomes quite error prone if the contained data is not plain-old-data, but has allocations.

It is still doable if we make the union and all the fields private. We would need to take care to overload (or make private):

  • The copy constructor
  • The move constructor
  • Copy assignment
  • Move assignment
  • Destructor

See https://en.cppreference.com/w/cpp/language/rule_of_three.

In other words, it would have to be something like:

namespace {  // private
  enum Rotation3DKind {
      Quaternion,
      AxisAngle,
  };
  
  union Rotation3DData {
      Quaternion quaternion;
      AxisAngle axis_angle;
  };
} // namespace

namespace rr {
    class Rotation3D {
      private:
        _kind: Rotation3DKind,
        _data: Rotation3DData,
  
      public:
        // Implicit constructors (maybe should be explicit?):
        Rotation3D(Quaternion&& quaternion) {
            _kind = Rotation3DKind::Quaterion;
            _data.quaternion = quaternion;
        }

        Rotation3D(AxisAngle&& axis_angle);

        Rotation3D(const Rotation3D& axis_angle); // TODO: copy-constructor
        Rotation3D(Rotation3D&& axis_angle) noexecpt; // TODO: move-constructor
        
        ~Rotation3D {
             switch self.kind {
                 Rotation3DKind::Quaterion:
                     self.data.quaterion.~Quaterion();
                     break;

                 Rotation3DKind::AxisAngle:
                     self.data.axis_angle.~AxisAngle();
                     break;
             }
        }


        Rotation3D& operator=(const Rotation3D& axis_angle); // TODO: copy-assignment
        Rotation3D& operator=(Rotation3D&& axis_angle) noexecpt; // TODO: move-assigment
    }
}

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions