Description
Issue description:
Most developers (ab)use enum
s to create aggregate user-defined types. There are few disadvantages to this method:
- it's a hack! enum structs are handled in an unconventional way and has absurd rules and special cases to make it work
- identifiers are spilled into global namespace
We can implement a new system to support user-defined aggregate types once in for all. To proceed we need to draft a spec for the syntax and semantics and also have consensus for the feature.
Here is my proposal (ad-hoc and not-well thought) to kick off discussions:
Tags are currently used for user-defined types. We can extend it to allow user-defined aggregate types.
/* static */ struct Player {
id,
playerid,
Float:health,
bool:spawned,
bool:isValid() { return id != 0; }
spawn(Float:pX, Float:pY, Float:pY) { /* do something */ }
Player:setHealth(Float:hp) { SetPlayerHealth(playerid, hp); return this; }
Player:setArmour(Float:ar) { SetPlayerArmor(playerid, ar); return this; }
static Player:create() { /* no 'this' here */ } // just syntactic sugar: Player.create()? do we even need this in the first draft?
static const Float:MAX_HEALTH = 100.0; // Player.MAX_HEALTH?
};
bool:operator==(Player:a, Player:b) { return a.id == b.id; } // operator overloads as member functions isn't required
sizeof(Player); // returns the size of the struct (number of cells)
tagof(Player:); // returns the tag id
new Player:x; // allocates a block of memory of sizeof(Player) -- practically equivalent to x[sizeof(Player)]
new Player:pInfo[MAX_PLAYERS]; // equivalent to x[MAX_PLAYERS][sizeof(Player)];
x.setHealth(100.0).setArmour(100.0);
doPlayerStuff(&Player:p) { } // passes the address
doPlayerStuff(Player:p) { } // passes a copy of the object in stack
We can have a new class for tags: aggregate-type tag and primitive-type tag. We can use the last but one bit to classify these (the MSB is currently used to mark STRONG and WEAK tags). Tag overrides b/w aggregate types might trigger a warning.
Questions:
- what should be printed by the following code:
new Player:x; main () { printf("%d", _:x); }
- Does pawn allow references to be returned?
- Does pawn allow operator overloads to accept references?
- What is
this
? What should be the output ofprintf("%d", _:this);
? - Is call-by-value as default newbie friendly?
- What does
new Player:a = Player:0;
mean? - How to embed debug information?
- What happens to an empty
struct
?
We could in principle implement the whole thing as an enum based struct internally and add special handling to prevent identifiers from being spilled out. This would mean passing an object by default would pass by reference.
Bonus: simulate namespaces with static members
struct Vector3f {
Float:x,
Float:y,
Float:z
Float:norm() { return VectorSize(x, y, z); }
};
struct Vehicle {
static const INVALID_ID = 65535;
static Create(modelid, Vector3f:pos) { return CreateVehicle(modelid, pos.x, pos.y, pos.z); }
static Create(modelid, Float:x, Float:y, Float:z) { /* ... */ } // in case we support function overloading for non-publics some day
static Destroy(id) { return DestroyVehicle(id); }
static bool:IsValid(id) { }
};
new Vector3f:position;
position.x = 123.0;
position.y = 555.0;
position.z = 1234.0;
new id = Vehicle.Create(420, position);
While this does simulate a namespace, it uses a .
instead of the _
convention for naming modules. I don't like this syntax but this is a possibility.
Bonus: unions with some tag overrides
struct TypeA {
Float:x
};
struct TypeB {
x
};
new TypeA:something;
new x = (TypeB:something).x;
We can similarly make variants by having a type
member variable.
Bonus: abuse to support polymorphism (needs more work but it seems to be possible)
struct BaseClass {
type,
something() {
switch(type) {
case 0: { /* something for base class -- maybe crash if u want base to be abstract? very bad but ... */ }
case 1: (DerivedClass:this).something();
}
}
};
struct DerivedClass {
type,
something() { }
}
Related issues:
- Tag-based OO Tag-based OO-like code #234
- Inheritance Feature proposal: enum expansion/inheritance #608