Closed richardanaya closed 4 years ago
maybe this is a bad example, but whats the vector for? You can just use an array.
using <stdio.h>::{printf};
struct Foo {
int a;
}
static Foo all_foo[100] = {0};
fn main() -> int {
for (usize mut i = 0; i < static(len(all_foo)) ; i++) {
printf("%d",all_foo[i].a);
}
return 0;
}
aep@horstbook3000: ~/kram/bla zz run
finished [Exe] bla
running "/Users/aep/kram/bla/target/test/bin/bla"
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000%
Ahh, sorry, here's a better example. In web assembly, we are relying upon external javascript calling exported functions from our code. Imagine I had some game.
extern rand() -> float;
struct Monster {
int x;
int y;
}
static Vec<Monster> all_monsters = [ null; 100 ]
export fn run_game_loop() {
// note that len() doesn't return capacity, it returns how many entities have been pushed
for (int mut i = 0; i < all_monsters::len() ; i++) {
// render monster
}
}
export fn key_pressed() {
new m = Monster {
x: (int)(rand()*100),
y: (int)(rand()*100)
};
all_monsters::push(m);
}
ah i see.
so first of all zz doesnt have templates. https://github.com/zetzit/zz#metaprogramming-or-templates-tail-variants
"template functions blow up code size and are difficult to export as plain C types"
in your case, you're making an array with a free marker. its a standard C construct. the Vec here is just adding high level sugar around that.
struct Monster {
bool used;
int x;
int y;
}
struct MonsterList+ {
Monster m[];
}
fn push (MonsterList+t mut * self )
{
// find free
}
static MonsterList+100 all_monsters = {0};
removing the boilerplate should be fairly simple with the new proc macros, but i wont put that into std, due to the code size blowup problem. As soon as we have package repo, everyone is free to create such a macro of course.
for the use case you have, a more convenient option is a pool
struct Monster {
int x;
int y;
}
static pool::Pool+100 all_monsters = {0};
export fn key_pressed() {
Monster mut * m = all_monsters.alloc(sizeof(Monster));
*m = Monster{
x: (int)(rand()*100),
y: (int)(rand()*100)
};
}
an in-between solution would be to use polymorphism, which is free in zz, while being very limited. you can construct something like a Vec generic with macros and polymorphism, but getting used to not using generic types is actually a good thing
i also realized people coming from rust might really trip over how stack ownership works in ZZ. you wrote:
new m = Monster {
x: (int)(rand()*100),
y: (int)(rand()*100)
};
all_monsters::push(m);
but thats a copy, not a move. you cannot allocate stack and move it. neither can rust, it just fakes it in an ugly way.
ZZ really is fundamentally C. You need to allocate memory at the same point as its lifetime. i.e. what you want is m to have the lifetime of all_monsters, so you must allocate it from memory owned by all_monsters
btw i just added block iterators to pool. which may be useful to your case.
here's a minimal http server using pool with comments.
let me know if this is understandable or if we need more docs or more syntactic sugar
using <stdio.h>::{printf};
using <assert.h>::{assert};
using string;
using address;
using err;
using tcp_server;
using tcp;
using io;
using io::unix;
using net;
using pool;
export fn main() -> int {
// make a pool of 1000 bytes for allocating tcp::Socket
new+1000 p = pool::make(sizeof(tcp::Socket));
new+1000 e = err::make();
new+10 async = unix::make();
new srv_addr = address::from_cstr("0.0.0.0:8083");
new server = net::tcp_server(net::os(), &e, &srv_addr, &async);
e.abort();
for (;;) {
// allocate a new socket
let client = (tcp::Socket mut *)p.alloc();
err::assert_safe(client);
if server.accept(&e, client) == io::Result::Ready {
e.abort();
new+100 saddr = string::make();
client->remote_addr.to_string(&saddr);
printf("accepted new client %x from %s\n", client, saddr.cstr());
} else {
// we didnt acutally need that allocation
p.free(client);
}
e.abort();
// iterate over all allocations
p.each(process_client, &p);
io::wait(&async, &e);
e.abort();
}
server.close();
return 0;
}
// for each currently allocated item in the pool (thats a socket)
fn process_client(pool::Pool mut *p, void mut *item, void mut *user)
where pool::member(item, p)
{
let client = (tcp::Socket mut *)item;
new+1000 e = err::make();
new+100 sbuf = string::make();
if client->recv(&e, &sbuf) == io::Result::Ready {
e.abort();
printf("%s\n", sbuf.cstr());
new+1000 msg = string::make();
msg.append_cstr("HTTP/1.1 200 OK\n");
msg.append_cstr("Connection: close\n");
msg.append_cstr("\n");
msg.append_cstr("ok cool\n");
client->send(&e, &msg);
client->close();
// note that its safe to manipulate the pool from the iterator
p->free(item);
}
e.abort();
}
generics are now supported via sized void. See https://github.com/zetzit/zz/blob/master/modules/map/src/main.zz
Vectors of structs seem like such a common paradigm. To elaborate on maybe an idea. Imagine we have some struct Foo.
I'd be curious how this is done today :)