Cello is the library that made the high-level C possible! Generics, parametric polymorphism, interfaces, constructors / destructors, garbage collector (optional), exceptions and reflection. Yes, yes, you heard right, all these flops in one bottle. Since Cello is built within standard C, the bottom line is that you get everything a living person needs on earth: high performance, powerful tools and flexible libraries.#include "Cello.h" int main(int argc, char** argv) { /* Stack objects are created using "$" */ var i0 = $(Int, 5); var i2 = $(Int, 3); var i2 = $(Int, 4); /* Heap objects are created using "new" */ var items = new(Array, Int, i0, i1, i2); /* Collections can be looped over */ foreach (item in items) { print("Object %$ is of type %$\n", item, type_of(item)); } /* Heap objects destructed via Garbage Collection */ return 0; } typedef . For example, let's write a structure for storing some kind of picture, yes on steroids! To do this, you need to define an ordinary sishny structure and register the newly acquired type using the Cello macro: struct Image { uint64_t width; uint64_t height; unsigned char *data; }; var Image = Cello(Image); Image and a variable that represents the type in runtime. By chance, we also called it Image . You probably paid attention to this suspicious comrade named var . In fact, var is just void* , that is, a generic pointer , but you should use the first option for convenience.Image : what's on the stack, what's on the heap. In addition, they can be printed, compared, thrown into the collection and that’s all: /* Allocate on Stack or Heap */ struct Image* x = $(Image, 0, 0, NULL); struct Image* y = new(Image); /* Print */ print("This is an image: %$\n", x); /* Compare */ print("Images %$ and %$ are equal? %s\n", x, y, eq(x, y) ? $S("Yes") : $S("No")); /* Put in an Array */ struct Array* a = new(Array, Image, x, y); print("Array of Images: %$\n", a); Image , contains a pointer to some area of memory that can be allocated by some other function. If you want to avoid leaks, you need to make sure that we free this memory in time. Now use Cello to define a destructor for Image : void Image_Del(var self) { struct Image* i = self; free(i->data); } self argument to a generic Image* type. This is possible because the Cello pointers (those that we create with var ) are fully compatible with the worker-peasant pointers in C. Since you have a var pointer from Cello, you know that there is a specific type hanging on it (just like here, in the destructor), which means that it is absolutely safe to bring it to this type and, of course, get access to the fields of this type. In this particular case, we call free for a pointer to the data from the Image .Cello macro, as a copy of the Instance new New class. Since we don’t want to define a constructor yet, we simply need to pass NULL to acc. field: var Image = Cello(Image, Instance(New, NULL, Image_Del)); Image object, it will call our destructor. And what, in my opinion, cool! #include "Cello.h" int main(int argc, char** argv) { /* Shorthand $ can be used for basic types */ var prices = new(Table, String, Int); set(prices, $S("Apple"), $I(12)); set(prices, $S("Banana"), $I( 6)); set(prices, $S("Pear"), $I(55)); /* Tables also support iteration */ foreach (key in prices) { var val = get(prices, key); print("Price of %$ is %$\n", key, val); } return 0; } #include "Cello.h" int main(int argc, char** argv) { var items = new(Array, Int, $I( 8), $I( 5), $I(20), $I(15), $I(16), $I(98)); /* Iterate over indices using "range" */ foreach (i in range($I(len(items)))) { print("Item Range %i is %i\n", i, get(items, i)); } /* Iterate over every other item with "slice" */ foreach (item in slice(items, _, _, $I(2))) { print("Item Slice %i\n", item); } return 0; } Source: https://habr.com/ru/post/262471/
All Articles