#Modifying a struct
1 messages · Page 1 of 1 (latest)
you can't mutate a type
a type is an immutable value
you can create a new one based off of the original of course
see @Type
🙏 thanks
also see @typeInfo for reflection of types
so what id do is id get the type info of the passed struct, edit it, and return @Type from that?
what im trying to do is make a gameobject sorta thing
so you can define your specific logic and have the framework add the necessary boilerplate fields and functions
but i also want the user to be able to access those automatically defined things in their code too
You can play with the fields but decls can't be reified with @Type
so theres no way to do what i want to do in a nice way?
well depends. Not in full generality like straight up code generation
which is unfortunate
would be nice if open ended types existed
as in
a type which you can add on to at compile time
that wouldn't make sense because of how the compiler works though. Types aren't vars.
But you can work around this kinda thing ususally
what do you suggest
if you have an idea of what kind of fields and decls need to be there then some combination of usingname and stuff like this can give you pretty decent semantics
like I'll need to see a concrete idea for what kinda types are you looking to generate first
simplified ecs, entities contain components, components contain data and logic
no like I mean specific examples
like can you give a brief example of what kind of structs you want to be able to mke
each component should have a destroy method provided by the framework (same between all components) and an entity field that points to the entity that owns the component
probably more eventually but these are the 2 basic ones
oh and also
the type itself should have a function for adding an instance of itself to an entity and a function to get itself from an entity
so essentially the whole package - methods, functions and fields
one idea i have now for methods and functions at least is:
pub const MyComponent = struct {
const Base = lib.Base(MyComponent);
usingnamespace Base;
}
lib.Base returns the functions/methods
for fields the user would just have to manually add them in themselves, i dont think theres a way around that?
not nessesarily, also have you explored stuff like @fieldParentPtr and various things like this? It can be used to make methods that upcast pointers to containing structs and stuff like this.
nope. ive been actively using zig for at most a week
i also find that the zig reference page is not that helpful, it contains the definitions but no examples of how to use the built-in functions
for most of them
its not great but its usuable. after a few weeks you would get the hang of it
okay I'll show you an example of this structfield that can find itself sort of thing.
one second
Okay here is an example of something you may find interesting:
const std = @import("std");
pub fn Component(comptime ParentContainer: type) type {
return struct {
pub const Self = @This();
pub const Parent = ParentContainer;
field1: usize = 0,
field2: []u8 = "",
const our_field_name: []const u8 = blk: {
for (std.meta.fields(Parent)) |field| {
if (field.type == Self) break :blk field.name;
}
@compileError("component does not exist in parent");
};
pub fn get_parent(self: anytype) switch (@TypeOf(self)) {
*Self => *Parent,
*const Self => *const Parent,
else => @compileError("bad type: " ++ @typeName(@TypeOf(self))),
} {
return @fieldParentPtr(Parent, our_field_name, self);
}
};
}
const Entity = struct {
const Self = @This();
component1: Component(Self),
field1: usize,
};
test "Going up!" {
var my_entity: Entity = .{ .component1 = .{}, .field1 = 2 };
const my_const_entity = my_entity;
const entity_ptr = &my_entity.component1;
const const_entity_ptr = &my_const_entity.component1;
try std.testing.expectEqual(&my_entity, entity_ptr.get_parent());
try std.testing.expectEqual(&my_entity, my_entity.component1.get_parent());
try std.testing.expectEqual(&my_const_entity, const_entity_ptr.get_parent());
try std.testing.expectEqual(&my_const_entity, my_const_entity.component1.get_parent());
}
doesnt look quite right
an entity is pretty much just an id, components are stored elsewhere, and components are what the user is meant to create
but i sorta get what you did there
you just have a sub struct with the boilerplate stuff
its just an example of how you can make these types that can do things like find the thing they are contained in
true
also if you know how it is implemented you could do all kinds of crazy polymorphic stuff in Vtables
well VTables are how it would actually compile down to from something like C# right
ive seen how allocator interfaces work on the inside and they use vtables
that stuff is COMPLICATED andi dont understand why
they have like an iterator for the vtable and some other stuff
why would they have that, why not just have a struct with function pointers
its something i noticed in a lot of places but couldn't really wrap my head around
There is some github issues around the design decisions
something to do with llvm optimizations
allocator isn't too complicated
its a "fat pointer" which If i recall correctly is also what rust does
it contains just 2 pointers, a type erased one to its allocator instance and a pointer to a vtable with the allocating functions (alloc, resize, free)
the library ill be making is mostly polymorphism free so in that regard it should be fine. getting all components on an entity will be interesting though
polymorphism free? So you are making something with all compile time type safety?
if i thought of everything correctly the api is polymorphism free except for getting all components on the entity
doing everything else shouldnt require polymorphism
also if you want to explore more avenues of metaprogramming other than Vtables you could try doing some gnarly magic with passing around function pointers everywhere with comptime hooks.
Alternative, you could go full python style ducktyping.
Instead of writing methods, just pass around your structs to anytype functions.
mostly looking to make something simple to understand, nice to use and performant
you could make something simple to use if you are willing to be flexible with simple to understand
heavy metaprogramming just doesn't tend to be simple to understand.
yeah maybe not
im thinking though
whats the nicest way to allow for getting all components on the entity
all of them, even if they are of different types
maybe having an interface with all the functions and the component stuff as a struct inside the component
similar to what you showed with entities and components
The allocator interface is pretty much just a struct of function pointers - it just puts that struct behind a const-pointer to save space, because it uses several function pointers, rather than just one that takes a 'mode'.
i tried most of the stuff previously mentioned and it was very ugly to work with sadly
Heavy generics are generally not a good idea really, to be honest.
Things can get unweidly or awkward quite fast. 😄
weeelp these aren't really heavy generics
the goal is to have a library type that you can extend and thats it
sounds like inheritance but nnnope
either that or users are forced to put a lot of boilerplate in their structs
i dont think zig has anything to make this sort of thing nice yet
To be honest, this sounds like your approach might just not be a great fit for Zig.
i dont want to do it a specific way. its just the way ive done it before in other languages
Right.
and im looking for an alternative that works in zig while still providing a nice api for the library user
You could use typeinfo and @hasDecl to identify if the user struct doesn't have the necessary namespaced functions.
Then if the user type doesn't have them, you can have your code that interacts with their struct call a fallback, for example.
It depends a bit on the specifics of what exactly you're trying to actually achieve here, of course - so I can only really give general advice.
falling back is definitely not an option
imagine a game object structure sorta like unity
you have entities, and entities can have components that let u do logic
so components need to have a bunch of predefined fields and methods
that the user will also be interacting with
A couple of other people have asked about how to do some ECS stuff in Zig.
Might be worth searching up on those; there's a couple of tricks that might be useful, like getting a unique ID per-type for example.
Though, I will mention the obligatory note that a full ECS is probably not what you need.
(https://www.youtube.com/watch?v=0S-KGLmLYnI)