| |
| //==================================================================== |
| // Library code: implementing the metaclass (once) |
| |
| $class interface { |
| constexpr { |
| compiler.require($interface.variables().empty(), |
| "interfaces may not contain data"); |
| for... (auto f : $interface.functions()) { |
| compiler.require(!f.is_copy() && !f.is_move(), |
| "interfaces may not copy or move; consider a" |
| " virtual clone() instead"); |
| if (!f.has_access()) f.make_public(); |
| compiler.require(f.is_public(), |
| "interface functions must be public"); |
| f.make_pure_virtual(); |
| } |
| } |
| virtual ~interface() noexcept { } |
| }; |
| |
| |
| //==================================================================== |
| // User code: using the metaclass to write a type (many times) |
| |
| interface Shape { |
| int area() const; |
| void scale_by(double factor); |
| }; |
| |
| // try putting any of these lines into Shape to see "interface" rules |
| // enforced => using the metaclass name to declare intent makes |
| // this code more robust to such changes under maintenance |
| // |
| // int i; // error: interfaces may not contain data |
| // private: void g(); // error: interface functions must be public |
| // Shape(const Shape&); // error: interfaces may not copy or move; |
| // // consider a virtual clone() instead |
| |
| // Godbolt.org note: Click the "triangle ! icon" to see the output |
| constexpr { |
| compiler.debug($Shape); |
| } |
| |
| |
| //==================================================================== |
| // And then continue to use it as "just a class" as always... this is |
| // normal code just as if we'd written Shape not using a metaclass |
| |
| class Circle : public Shape { |
| public: |
| int area() const override { return 1; } |
| void scale_by(double factor) override { } |
| }; |
| |
| #include <memory> |
| |
| int main() { |
| std::unique_ptr<Shape> shape = std::make_unique<Circle>(); |
| shape->area(); |
| } |