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

    imageCello — это библиотека, которая сделала высокоуровневый 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*, тоесть обобщенный указатель, но стоит использовать первый вариант, для удобства.

    В контексте базовых типов, это все. Больше ничего не нужно писать, за тебя все сделает компилятор. Теперь можно создавать переменные типа 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 довольно долгое время экспериментировали, так что в принципе, это штука рабочая. Я думаю, что фуллтайм С-программисты однозначно должны заценить.

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

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

    Метки:
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 58
    • +3
      Всё что угодно кроме «Определенно, да» :)))
      • –3
        Не буду из-за одного только var.
        Хотя в целом плюшки приятные.
        Не хокку.
        • +3
          typedef void* var;
          

          Это ещё не самое ужасное. Вот, смотрите:

          #define is ==
          #define isnt !=
          #define not !
          #define and &&
          #define or ||
          #define in ,
          

          Зачем?
          • +3
            #define in,
            о_0
            Тяжёлая наркомания. Либо это не перечисление, а что-то другое, но тогда наркомания тяжелее тяжёлой.
            • +6
              Это типа для foreach (element in collection)
              • –11
                Обосраться, я не успеваю писать комментарии, а ты уже все раскрываешь!
            • +8
              Конкретно is, isn't, not, and и or — конечно же, полная ересь. Но in нужен для того, чтобы писать красивые foreach-циклы!
              • 0
                int main(int argc in char** argv)

                Из-за этого возникает ощущение, что «оллимпиадник» Cello сделал. Они любят… такое.
                • +2
                  Ага,
                  var a in *b;
                  

                  Или макросы вызывать:
                  macro(in in in)
              • +14
                Между прочим,
                Alternative spellings для логических операторов есть в стандарте самого С, определены в <iso646.h>

                Primary 	Alternative
                &&	and
                &=	and_eq
                &	bitand
                |	bitor
                ~	compl
                !	not
                !=	not_eq
                ||	or
                |=	or_eq
                ^	xor
                ^=	xor_eq
                
                • +1
                  А в С++ они даже являются не макросами, а полноценными операторами.
                  • –2
                    Ещё есть триграфы. В библиотеке это говно зачем?)
                • –7
                  Тебе никто не запрещает использовать рабоче-крестьянский void*. Я конечно не ручаюсь в таком случае за твое здоровье, но твое право!
                  • +1
                    Что void*, что var — код не читабелен. Поди угадай, что за тип. Я бы предпочёл настоящую строгую типизацию, чем вообще её отсутствие.
                    • 0
                      Какое отношение строгая типизация имеет к явным аннотациям типов?
                      • 0
                        Такое, что ни в С, ни в С++ нет строгой типизации. Я бы предпочёл «дополнение» со строгой типизацией, чем всякую прочую ненужную фигню.

                        Например, какой тип будет при записи: «var a = 1;»? Под это объявление подходит минимум восемь типов int'ов, плюс float и double (но их точно не будет выбрано).
                        • 0
                          В языке с действительно строгой типизацией вы не напишете var a = 1.5;, а в хорошем языке со строгой типизацией var a = 1; даст полиморфный тип (как тайпкласс Num в хаскеле, например).

                          Строгую типизацию ни в плюсы, ни в C уже не впилить при всём желании, к сожалению. Но судить об этом по var или auto не стоит.
                          • 0
                            Я и не спорю, что var (почти) не имеет общего со строгой типизацией. В С++ строгую типизацию добавить элементарно на уровне компилятора. Только потеряется совместимость с имеющимися программами и придётся в большинстве арифметических операций вручную тип задавать… Пожалуй, мне и без строгой типизации неплохо программируется.
                            • 0
                              Согласен, явное преобразование типов способно создать больше геморроя, чем отсутствие строгой типизации.
                              • 0
                                А совместимость с имеющимися программами, библиотеками и кодом — одна из основных причин, почему люди выбирают С и С++, и почему С++ вообще взлетел в своё время.

                                А зачем задавать типы руками?
                                • 0
                                  double a = 5.0 + 5;
                                  При строгой типизации не скомпилируется, придётся писать double(5).
                                  • 0
                                    И в чём удобство такого?
                                    • 0
                                      А где я говорил, что это удобно? Я говорил, что мне перехотелось строгой типизации, глядя на это.)
                                    • +3
                                      Prelude> 5.5 + 5
                                      10.5
                                      Prelude> let a = 5
                                      Prelude> let b = 5.5
                                      Prelude> a + b
                                      10.5
                                      Prelude> :t a
                                      a :: Num a => a
                                      Prelude> :t b
                                      b :: Fractional a => a
                                      Prelude> :t a + b
                                      a + b :: Fractional a => a
                                      


                                      Чудно!

                                      Это Haskell, если что, строже особо некуда.
                    • +12
                      ШОК! Зачем же мне теперь все эти ваши Go/D/Nim/<впиши>, если С на стероидах решает все проблемы рода человеческого?!
                      Cello добавляет поверх С дополнительный слой рантайма.
                      мда… проблема рода человеческого в том, чтобы обеспечить почти всё это без рантайма.
                      • +4
                        Есть же Rust, который делает всё это и куда больше, только без оверхеда в рантайме. И как, кстати, у Cellо дела со строгой типизацией? Сдается мне, что многие синтаксические фишки не позволяют сохранить информацию о типе без поддержки компилятора, как тот же var.
                        • –6
                          Тоесть ты сейчас серьезно критикуешь, по-сути, Си, за то, что в нем нету плюшек из Rust? Тут товарищ сделал костыль для сишки и назвал его Cello. Зараза, в каждый тред о C/C++ прибежит фрик, угорающий по Rust и начнет доказывать, что последний обосраться какой крутой.

                          Все понимают, что в Rust с его девятью типами указателей полная потокобезопасность, контроль лайфтайма и вот это все. Ясно, всем все понятно, спору нет — классный язык. Не нужно об этом постоянно напоминать.
                          • +5
                            Критикую статью за голословные восторгания, поскольку у Cello-таки остались нерешенные проблемы рода человеческого, а альтернативы уже объявлены ненужными. Я радею отнюдь не за превосходство Rust, Nim или любого другого инструмента над остальным, а за объективность статей на хабре.
                            • –10
                              Тоесть весь дух статьи, теги, все вот это… не натолкнули тебя на мысль, что может где-то я могу как-то преувеличивать значимость чего-то или где-то могу как-то повыгоняться? Каких же скучных сейчас делают человеков.
                              • 0
                                Эллочка по-рабоче-крестьянски завидует тебе, что слог так прекрасен, и вот это все.
                        • +2
                          Си, в основном, используется там, где нужна прямо таки maxxxимальная производительность — например, во встраиваемых системах.
                          Там он используется скорее потому, что компилятора C++ (и Rust, ага) просто нет, или есть, но «ужас-ужас-ужас».
                          • +14
                            Получается, нишу C это заполнить не может (рантайм, гц, динамическая типизация). Зачем оно тогда вообще нужно? С такими свойствами полно других языков, только не являющихся набором костылей над С.
                            • +2
                              Как-то пробовал на нём писать «Песню про пиво», ощущения так себе.
                              • –1
                                Всегда считал, что вместо траты времени на создание новых языков, лучше написать хорошую библиотеку для того языка, который тебе нравится.

                                Cello — прикольная штука, думаю это первая ласточка к созданию «упрощенных / безопасных» библиотек / оберток для С и С++.

                                Там где нужна скорость — пишешь на чистом С / С++;
                                Там где нужна супер скорость — пишешь asm {… }
                                Там где скорость не критична, подключаешь Cello.h или еще что-то и быстренько ваяешь что-нибудь в стиле PHP.
                                И все это в одной программе и на одном компиляторе — вот это было бы супер круто!!!

                                На С писал программы только по работе для микроконтроллеров (других применений его в 21 веке не знаю), там скорость очень важна, поэтому Cello использовать никто не будет.

                                А вот на C++ пишется много прикладных задач, в которых критических мест мало, вот там можно было бы добавить хорошую библиотеку типа php.h

                                PHP чем хорош? Тем что там по любому чиху уже есть готовая стандартная ф-ция, не надо искать библиотеку (выбираешь из стандартных), не надо следить за освобождением памяти, автоматические типы переменных, простой интерфейс для ассоциативных массивов с теми же foreach и т.п…

                                Когда начинал писать на PHP все время задавался вопросом: Почему такую же библиотеку не сделать для С++? Ведь по сути все стандартные библиотеки PHP написаны на С++, зачем придумывать новый язык если можно сделать удобную обертку, ибо C++ велик и могуч! :)

                                В новомодном C++14, много фишек типа всяких лямбда и новой интерпретации auto, но имхо, их синтаксис туманит разум выросший на простом и понятном C++98. Вот если бы эти фишки применить для обертки библиотек php и их автоматической конвертации в код C++ по типу Cello… А там уже и не далеко до полного портирования любого php-кода в C++ (если разницу в синтаксисе через обертку минимизировать, то может даже до простого copy/past можно довести).

                                Ну что, господа, как Вам идея?
                                А может такая штука уже давно реализована, и я один ни х… не знаю? :)
                                • 0
                                  Украл мой комментарий :-).
                                  • +1
                                    О… пояндексил и нашел phpcpp.h
                                    правда он для создания расширений на cpp для php сервера, и… не так удобен как хотелось (если бы просто повторить/приблизиться к синтаксису php), поэтому обычное использование библиотеки чисто для создания c++ программ не дает особого выигрыша в простоте
                                    • +8
                                      Вы предлагаете решать все задачи с помощью одного известного вам инструмента, потому что он «велик и могуч», но серебряной пули не бывает. Для написания сайта нужен совсем другой фреймворк, нежели для десктопного приложения или мобильной игры, и попытка сделать один универсальный приведет только к тому, что на нем будет одинаково сложно и неудобно писать любые приложения.
                                      • –3
                                        Ну… не знаю.
                                        Основной инструмент, который я использую для C++ это С++Builder.
                                        С помощью него сейчас из коробки можно написать и десктопное приложение (Vcl, FireMonkey), сайт (IntraWeb/VclForWeb, Indy) и мобильную игру (компилится в нативный, android, mac) и компоненты для работы с БД (SQLite, MySql, FireBird, MS SQL...)
                                        И все это я реально использую в своих проектах из одной IDE и на одном языке.

                                        PHP — юзаю больше для создания прототипов и доработки open source проектов используемых в нашей организации. Плюсы PHP, которых мне не хватает в C++ я описал выше.

                                        А то что С++ «велик и могуч» — это Вы не сомневайтесь :)
                                        На его базе с помощью оберток можно к синтаксису любого языка приблизиться (так сказать создать язык внутри языка), было бы время да голова на плечах.
                                        • +3
                                          2015
                                          • 0
                                            :) Других аргументов нет?
                                            • 0
                                              Пишу в 2015 на C++. И на Haskell ещё. Что не так?

                                              Впрочем, не нужны все эти обёртки там, и мне, как выросшему на C++03, все эти фишки из C++11/C++14 голову не туманят совершенно. Вопрос практики, ИМХО.
                                        • +4
                                          Посмотрите в сторону Vala. Не совсем то, но транслируется в си, потом компилируй чем хочешь.
                                          • –3
                                            Спасибо. Интересная штука, но действительно… не совсем то, чего душа требует.
                                            Хочется просто подключить xxx.h и пользоваться плюшками, а не залазить в другую IDE, транслировать из нее, потом компилить, чтоб изменения проверить… неудобно.
                                          • +1
                                            В данном случае основной недостаток C++ — что он не отсекает возможность писать небезопасный код. На фоне этой небезопасности в плане «диких» указателей и утечек памяти все «улучшения через макросы» выглядят как «ложка мёда к бочке дёгтя» — ни на хлеб положить ни телегу смазать. Как если бы это были макронадстройки над языком ассемблера. И как, в прочем, и аналогичные надстройки на фортом, которые регулярно появляются just for fun.
                                            • –1
                                              Не безопасный код — понятие растяжимое, его можно писать на любом языке.
                                              Типа взаимный Lock потоков из-за неверного условия в if Или случайно затерявшаяся строчка посреди программы exec(«format c:») :-)… Все зависит от того, кто этот код пишет.

                                              Дикие указатели с new и забытыми/не продуманными delete — удел школьников и дилетантов.
                                              Профессионалы если такое используют — то делают это обосновано (очень очень нужна скорость) проектируют и тестируют архитектуру для всех возможных вариантов событий на 100 ходов веред.

                                              Там где скорость не критична и более важно удобство — ВСЕГДА нормальными программистами используются смарт-поинтеры (причем при правильной архитектуре достаточно лишь auto_ptr) и контейнеры с автоочисткой.

                                              Надстройка (доп. библиотека) нужна лишь только для того, чтобы упростить синтаксис работы с этими указателями и контейнерами (в стандартном варианте он получается длинноват)

                                              Мне приходилось писать на разных ЯП от asm, бэйсика, паскаля, js, vb, c#, php, до языков функциональных блоковых диаграм и LAD. Но оптимальным для себя выбрал именно c++ в совокупности с IDE С++Builder которая всегда в авангарде технологий и имеющая кучу готовых компонентов, которые на данный момент позволяют решать абсолютно любые современные задачи за вполне разумное время (от построения дестопного GUI или web-сервисов до программирования МК или мобильных устройств).

                                              Для счастья всего лишь не хватает библиотеки, которая упростила бы (сократила код) для работы в критически не важных по скорости местах программы по типу php и его стандартных библиотек.

                                              Vala — это не совсем то. Основная ее идея — полное создание приложения на Vala (с возможностью вызова C ф-ций) и последующей компиляции в продакшен на C. Я же говорю об основном коде на C++ и подключением к этому коду библиотеки обеспечивающей упрощение синтаксиса и автоматическую безопасность. Что упростит и разработку и отладку.
                                              • +1
                                                unique_ptr только, пожалуйста, вместо auto_ptr.
                                                • –1
                                                  Спасибо за бдительность!
                                                  unique_ptr эт для С++11, в моем случае поддерживаемые проекты чуток по старше :)
                                            • 0
                                              Подобные бубнотанцы с макросами могут быть нужны, если вдруг необходима компиляция кода, написанного на более безопасном языке типа Java или C#, компилятором С или С++. Типа трансляции в Javascript в GWT. Тут гарантией безопасности является компилятор исходного языка, а «вылизать» транслятор и макробиблиотеку теоретически нужно только один раз.
                                            • +2
                                              Для продакшена есть С++ (даже без D, Go, Rust и Nim). Еще есть C++/Boost, в котором тоже немало накручено… но он как-то используется более широко, поэтому доверия больше.
                                              А это — любопытно, не более того. Любопытный пример того, что должно быть в языках программирования, но нет, отличный довод в пользу того, что в языках должно быть много фич (аналогично Бусту для С++).
                                              • +3
                                                Человек сделал vala, причем сделал намного хуже оригинала. Не нужно
                                                • 0
                                                  во, а я все думал, где я про похожее уже читал. Точно, vala получше будет.
                                                  • 0
                                                    Все были бы благодарны, если бы Вы написали бы обзорную статью про vala.
                                                  • 0
                                                    Да кто хотел изучить что-то помимо C, давно изучил, и продолжают изучать новые языки. Кто не хочет — тем хоть что предложи, найдут аргументы против.
                                                    Это я не к тому, что описанный в статье велосипед хорош.
                                                    • 0
                                                      Согласно Десятому правилу Гринспана, лучшей и наиболее естественной надстройкой над фортраном или С является Common Lisp ;-). Ну или в крайнем случае другой функциональный язык ;)

                                                      А всё почему? А потому, что незаметно, чтоб авторы языка С удосужились сделать нормальную поддержку использования блоков кода в качестве аргументов макроса.
                                                      • 0
                                                        А как со скоростью такой надстройки? Вы точно уверены что речь о надстройке над C, а не над Forth? Поскольку функциональный код можно очень легко транслировать в быстрый Forth-код, просто превратив выражения вроде +(a,b) в код вида a b +
                                                        Но вот способа превратить функциональный код в быстрый C-код я не вижу.
                                                      • 0
                                                        Хочу! (^_^)

                                                        А с OpenGL эта библиотека насколько хорошо совместима? (кто-нибудь проверял?)
                                                        • 0
                                                          Таки скачал, попробовал парочку своих (и вполне живых в продакшне) сишностей к этому приспособить.
                                                          В общем, поигрался и выплюнул. Голосовал как «Посмотрю в его сторону и может быть…».
                                                          • 0
                                                            Накатал примерно за 10 минут простейший foreach :)
                                                            #include <stdio.h>
                                                            
                                                            #define in ,
                                                            #define with
                                                            #define size ,
                                                            #define foreach(...) foreach3args(__VA_ARGS__)
                                                            #define foreach3args(iter, array, size) for (int i = 0, iter = array[i]; i < size; i++, iter = array[i])
                                                            
                                                            int main(int argc, char** argv) {
                                                              int arr[5] = {1, 2, 3, 4, 5};
                                                              int a;
                                                              
                                                              foreach (a in arr with size 5) {
                                                                printf("%d\n", a);
                                                              }
                                                            }
                                                            

                                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.