Cello — это библиотека, которая сделала высокоуровневый C возможным! Обобщения (generics), параметрический полиморфизм, интерфейсы, конструкторы/деструкторы, сборщик мусора (по желанию), исключения и рефлекция. Да-да, ты не ослышался, все эти плюхи в одном флаконе. Так как Cello построен в пределах стандарта С, в сухом остатке ты получишь все, что нужно живому человеку на земле: высокую производительность, мощный инструментарий и гибкие библиотеки.
Talk is cheap, show me the code!
ШОК! Зачем же мне теперь все эти ваши Go/D/Nim/<впиши>, если С на стероидах решает все проблемы рода человеческого?! Хочешь узнать о готовности Cello к продакшну и увидеть еще больше кода? Добро пожаловать подкат.
Cello добавляет поверх С дополнительный слой рантайма. Это абсолютная необходимость, потому что иначе расширить язык было бы возможно только меняя компилятор, а такую роскошь мы себе позволить не можем. Пользователь определяет типопеременные (runtime type variables), которые содержат всю необходимую информацию для нового функционала, связывая их с обычными легитимными типами.
Оверхед у GC в Cello конечно же, есть. Указатели в Cello сопровождаются дополнительной мета-информацией, которая хранится прямо перед данными. Это говорит о том, что указатели в Cello полностью совместимы с рабоче-крестьянскими указателями из Стандарта и могут без труда кооперироваться.
Вся информация о типах в большей мере является всего лишь списком экземпляров типоклассов и интерфейсов. Эти ребята себя очень хорошо зарекомендовали, поэтому они используются везде: начиная от GC, заканчивая документацией. Они позволяют использовать объекты в контексте их поведения. Это прикольно, потому что можно писать алгоритмы в обобщенной манере, опираясь только на вводные данные, но не на реальную реализацию той или иной структуры.
Cello, в принципе, довольно умен и может автоматически выводить поведения (behaviours) в большинстве случаев. Объекты в Cello можно печатать, сравнивать, хэшировать, сериализировать, сортировать копировать и вот это все. Короче говоря, райское наслаждение.
Объекты в Cello начинаются с обыкновенных рабоче-крестьянских структур, которые ты так хорошо знаешь из С. Чуть позже Cello добавит в них немного мета-информации. Тут есть одно требование: определять структуры нужно без
Обрати внимание, у нас появились две штуки. Оригинальный сишный тип
В контексте базовых типов, это все. Больше ничего не нужно писать, за тебя все сделает компилятор. Теперь можно создавать переменные типа
В действительности, почти все основные типоклассы в Cello, по умолчанию, с реализацией. Но настоящая сила Cello проявляется тогда, когда мы начинаем расширять реализацию этих типоклассов.
Наша сишная структура,
Ты можешь с легкостью привести аргумент
Чтобы зарегистрировать деструктор в Cello, ты захочешь передать его в макрос
Теперь, когда GC в Cello придет, чтобы разобраться с объектом
Daniel Holden написал Cello, чтобы местами упростить свою работу, так что тут хватает разнообразного сахара. Например, сокращенный синтаксис создания переменных или даже таблицы (sic!):
Или замысловатые range-циклы и прочие слайсы:
На самом деле, возможности Cello не заканчиваются на приведенном мною в этой статье функционале, но это не беда, ведь с остальными штуками вы сможете ознакомиться с помощью документации. Кстати говоря, у Cello есть классный Quickstart, в котором автор покажет, как написать програму, которая интересным образом глитчует .tga-изображения. Настоятельно рекомендую ознакомиться!
Отвечая на вопрос, готов ли Cello к продакшну… тут нет однозначного ответа. Си, в основном, используется там, где нужна прямо таки maxxxимальная производительность — например, во встраиваемых системах. Захотят ли разработчики подобного софта тянуть за собой GC — это очень спорный вопрос и я склонен к отрицательному ответу. С другой стороны, над 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*
, тоесть обобщенный указатель, но стоит использовать первый вариант, для удобства.В контексте базовых типов, это все. Больше ничего не нужно писать, за тебя все сделает компилятор. Теперь можно создавать переменные типа
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? %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);
В действительности, почти все основные типоклассы в 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 %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;
}
И это еще далеко не все...
На самом деле, возможности Cello не заканчиваются на приведенном мною в этой статье функционале, но это не беда, ведь с остальными штуками вы сможете ознакомиться с помощью документации. Кстати говоря, у Cello есть классный Quickstart, в котором автор покажет, как написать програму, которая интересным образом глитчует .tga-изображения. Настоятельно рекомендую ознакомиться!
Отвечая на вопрос, готов ли Cello к продакшну… тут нет однозначного ответа. Си, в основном, используется там, где нужна прямо таки maxxxимальная производительность — например, во встраиваемых системах. Захотят ли разработчики подобного софта тянуть за собой GC — это очень спорный вопрос и я склонен к отрицательному ответу. С другой стороны, над Cello довольно долгое время экспериментировали, так что в принципе, это штука рабочая. Я думаю, что фуллтайм С-программисты однозначно должны заценить.
Для тех, кому интересно, как эта шняшка устроена внутри, ссылочка на гитхаб. Помимо всего прочего, я также хотел бы сделать небольшой опрос по теме поста. Прошу отвечать на него только разработчиков, которые реально работают с языком С, остальных хочу попросить воздержаться.
Only registered users can participate in poll. Log in, please.
Cтанете ли вы использовать Cello?
8.17% Определенно, да!46
32.5% Конечно же, нет!183
27.18% Посмотрю в его сторону и может быть…153
2.13% Врежу тимлиду сковородой по голове и пущу в продакшн12
30.02% Этот чувак сломал наш С!!! Бей его!!!169
563 users voted. 248 users abstained.