Высокоуровневый С или пару слов о Cello

Высокоуровневый С или пару слов о Cello

497


Cello — это библиотека, которая сделала высокоуровневый C вероятным! Обобщения (generics), параметрический полиморфизм, интерфейсы, конструкторы/деструкторы, сборщик мусора (по желанию), исключения и рефлекция. Да-да, ты не ослышался, все эти плюхи в одном флаконе. Так как Cello построен в пределах эталона С, в сухом остатке ты получишь все, что нужно живому человеку на земле: высшую производительность, мощный инструментарий и гибкие библиотеки.

Talk is cheap, show me the code!

#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;
}

ШОК! Для чего же мне теперь все эти ваши Go/D/Nim/<впиши>, раз С на стероидах решает все проблемы рода человеческого?! Хочешь выяснить о готовности Cello к продакшну и увидеть еще больше кода? Добро пожаловать подкат.

Вводная
Cello добавляет поверх С доп слой рантайма. Это абсолютная необходимость, потому что по другому расширить язык было бы возможно только меняя компилятор, а такую роскошь мы для себя позволить не можем. Пользователь определяет типопеременные (runtime type variables), которые содержат всю нужную информацию для нового функционала, связывая их с обычными легитимными типами.

Оверхед у GC в Cello естественно же, есть. Указатели в Cello сопровождаются дополнительной мета-информацией, которая хранится прямо перед данными. Это говорит о том, что указатели в Cello на сто процентов совместимы с рабоче-крестьянскими указателями из Стандарта и могут без труда кооперироваться.

Вся информация о типах в большей мере является всего только списком экземпляров типоклассов и интерфейсов. Эти ребята себя чрезвычайно хорошо зарекомендовали, поэтому они используются везде: начиная от GC, заканчивая документацией. Они разрешают использовать объекты в контексте их поведения. Это прикольно, поэтому что можно писать алгоритмы в обобщенной манере, опираясь лишь на вводные данные, но не на реальную реализацию той либо иной структуры.

Cello, в принципе, довольно умен и может автоматом выводить поведения (behaviours) в большинстве случаев. Объекты в Cello можно печатать, ассоциировать, хэшировать, сериализировать, сортировать копировать и вот это все. Короче говоря, райское удовольствие.

Объекты
Объекты в Cello начинаются с обыкновенных рабоче-крестьянских структур, которые ты так отлично знаешь из С. Чуть позже Cello добавит в них незначительно мета-информации. Тут есть одно требование: определять структуры необходимо без typedef. Например, давайте напишем структуру для хранения какой-то рисунки, да на стероидах! Для этого нужно определить обычную сишную структуру и зарегистрировать новополученный тип при помощи макроса Cello:

struct Image {
uint64_t width;
uint64_t height;
unsigned char *data;
};

var Image = Cello(Image);

Обрати внимание, у нас возникли две штуки. Оригинальный сишный тип Image и переменная, которая представляет тип в рантайме. По воле варианта, мы тоже ее назвали Image. Ты скорее всего направил внимание на этого подозрительного товарища по имени var. На самом деле var это всего только void*, тоесть обобщенный указатель, но стоит использовать 1-ый вариант, для удобства.

В контексте базовых типов, это все. Больше ничего не необходимо писать, за тебя все сделает компилятор. Теперь можно создавать переменные типа Image: что на стэке, что на куче. Кроме всего прочего, их можно напечатать, сравнить, закинуть в коллекцию и вот это все:

/* 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? %sn",
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);

В реальности, почти все основные типоклассы в Cello, по умолчанию, с реализацией. Но реальная сила Cello проявляется тогда, когда мы начинаем расширять реализацию этих типоклассов.

Конструкторы и деструкторы
Наша сишная структура, Image, содержит указатель на какой-то участок памяти, который может быть выделен какой-то иной функцией. Если ты хочешь избежать утечек, надо убедиться, что мы впору эту память освобождаем. Теперь воспользуемся Cello, чтобы найти деструктор для Image:

void Image_Del(var self) {
struct Image* i = self;
free(i->data);
}

Ты можешь с легкостью привести аргумент self к сишному типу Image*. Это может быть, потому что указатели Cello (те, которые мы создаем с var) на сто процентов совместимы с рабоче-крестьянскими указателями в С. Так как у тебя есть var-указатель из Cello, ты знаешь, что на нем висит опредленный сишный тип (прямо как тут, в деструкторе), а значит, что можно абсолютно безопасно привести его к этому типу и очевидно, получить доступ к полям этого типа. В конкретно этом случае, мы вызываем free для указателя на данные из Image.

Чтоб зарегистрировать деструктор в Cello, ты захочешь передать его в макрос Cello, как экземпляр Instance новейшего типокласса New. Так как мы пока не хотим определять конструктор, то стоит просто передать NULL в соотв. поле:

var Image = Cello(Image, Instance(New, NULL, Image_Del));

Сейчас, когда GC в Cello придет, чтобы разобраться с объектом Image, он вызовет наш деструктор. А чего же, по-моему, круто!

Cахар, сахар, сахар
Daniel Holden написал Cello, чтоб местами упростить свою работу, так что тут хватает различного сахара. Например, сокращенный синтаксис создания переменных или даже таблицы (sic!):

#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;
}

Либо замысловатые range-циклы и прочие слайсы:

#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 %in", i, get(items, i));
}

/* Iterate over every other item with "slice" */
foreach (item in slice(items, _, _, $I(2))) {
print("Item Slice %in", item);
}

return 0;
}

И это еще далековато не все…
На самом деле, возможности Cello не заканчиваются на приведенном мною в данной статье функционале, но это не беда, ведь с остальными штуками вы можете ознакомиться с помощью документации. Кстати говоря, у Cello есть потрясающий Quickstart, в котором автор покажет, как написать програму, которая увлекательным образом глитчует .tga-изображения. Настоятельно рекомендую ознакомиться!

Отвечая на вопросец, готов ли Cello к продакшну… тут нет однозначного ответа. Си, в основном, употребляется там, где нужна прямо таки maxxxимальная производительность — к примеру, во встраиваемых системах. Захотят ли разработчики подобного софта тянуть за собой GC — это чрезвычайно спорный вопрос и я склонен к отрицательному ответу. С другой стороны, над Cello достаточно долгое время экспериментировали, так что в принципе, это штука рабочая. Я думаю, что фуллтайм С-программеры однозначно должны заценить.

Для тех, кому интересно, как эта шняшка устроена снутри, ссылочка на гитхаб. Помимо всего прочего, я также желал бы сделать небольшой опрос по теме поста. Прошу отвечать на него лишь разработчиков, которые реально работают с языком С, остальных хочу попросить воздержаться.

Cтанете ли вы применять Cello?

Определенно, да!

Конечно же, нет!

Посмотрю в его сторону и может быть…

Врежу тимлиду сковородой по голове и пущу в продакшн

Этот чувак сломал наш С!!! Бей его!!!

Проголосовало 106 человек. Воздержалось 43 человека.

Лишь зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

habrahabr.ru