Часто ли у вас возникает потребность в написании фрагмента кода с целью:
Лично я попадаю в такую ситуацию довольно часто. И как правило, для этих целей приходится запускать Visual Studio (либо другую IDE, что не суть важно), создавать проект, директорию для него, а также файл с реализацией main функции. Не сложно, но хлопотно. Также иногда требуется включить ряд залоговочных файлов или прилинковать библиотеки в проект. Ну и конечно, не факт что проект сбилдится и запустится сразу же, а не потребует исправлений в путях, либах и т.д.
Соласитесь, было бы гораздо удобнее воспользоваться интерпретатором (интерактивной оболочкой, interactive shell). Возьмите, к примеру Ruby, Python или даже Haskell. Для начала полноценной работы с этими языками достаточно лишь напечатать в командной строке irb, python и ghci соответственно. Было бы здорово иметь такую же возможность при работе с C\C++.
Ну что я могу сказать. Такой интерпретатор уже есть! Это cling и цель данного поста — познакомить читателя с его основными возможностями.
cling — C++ интерпретатор (C++ LLVM-based Interpreterg), основанный на проектах clang/LLVM. Сlang же в свою очередь является C++ фронтэндом LLVM. И хотя и clang и LLVM не входят в “область определения” (scope) данного поста, тем не менее, думаю будет полезно узнать что скрывается за ними.
Для понимания, что такое фронтэнд clang, и зачем нужна связка LLVM->Clang->Cling, надо понимать суть проекта LLVM и какой цели он служит.
LLVM (Low-Level Virtual Machine). Данный проект представляет собой целую программную кроссплатформенную инфраструктуру (компилятор, toolchain, различные тулзы). Написан на С++. Принцип работы LLVM заключается в оптимизации промежуточного кода (Intermediate Representation, IR), полученного в результате работы одного из его фронтэндов (например, clang‘а - для С++). Причем, это внутреннее представление может быть скомпилировано в конечный машинный код для конкретной платформы либо исполняться по типу JIT компиляции. В качестве очень интересного примера можно привести RubyMotion. RubyMotion реализует поддержку Ruby в среде LLVM. Это дает прекрасную возможность пользователям писать нативные iOS приложения на Ruby (для интересующихся, в конце поста есть ссылка на пост про RubyMotion).
Цитата с русской Википедии:
В основе LLVM лежит промежуточное представление кода (Intermediate Representation, IR), над которым можно производить трансформации во время компиляции, компоновки и выполнения. Из этого представления генерируется оптимизированный машинный код для целого ряда платформ, как статически, так и динамически (JIT-компиляция).
LLVM написана на C++ и портирована на большинство nix-систем и Windows. Система имеет модульную структуру, отдельные ее модули могут быть встроены в различные программные комплексы, она может расширяться дополнительными алгоритмами трансформации и кодогенераторами для новых аппаратных платформ.
В принципе, можно реализовать свой собственный язык программирования и подключить его к LLVM, написав к нему Front-end, переводящий код в IR представление. При этом, конечно, придется проштудировать мануал LLVM.
Clang — С/Objective C/C++ фронтэнд (Front-end) разработанный как часть проекта LLVM. Однако, при использовании в командной строки представляет собой полноценный компилятор (сам фронтэнд, оптимизатор и линкер). Переводит код на упомянутых языках в IR представление, и далее с помощью бэкэнда (Back-end) уже в конечный результат.
Clang обладает многими интересными фичами и его можно использовать по разному. Например, как вам идея использовать его в качестве неплохого статического анализатора C (Clang Static Analyzer) в своих решениях? Или включить в проект возможность парсинга исходного текста на С++ с выводом информации об AST-дереве, используя Clang API?
Мы обязательно рассмотрим и LLVM и Clang более подробно в последующих постах.
Вообще, интерпретатор Cling разработан в недрах ЦЕРНа (cern.ch) и в рамках замены существующего интепретатора CINT, входящего в состав фреймворка ROOT.
ROOT же представляет собой огромный фреймворк созданный инженерами ЦЕРНа для упрощения анализа огромных и непрерывно поступающих объемов информации с помощью обширнейшего инструментария (математические, графические библиотеки). ROOT — это и командный интерпретатор (на манер Matlab'овского), реализующий всю функциональность фреймворка и предоставляющий всю его мощь, и набор библиотек для включения в свои проекты. Через некоторое время я планирую начать цикл статей, посвященный использованю этого, поистине, огромного фреймворка. Так что, stay turned!
Так как Cling является проектом ЦЕРНа, то у нас есть два варианта его установки:
Насчет второго варианта заморачиваться мы не будем - ROOT нам сейчас не нужен. Для получения бинарника, необходимо отстроить связку LLVM + Clang + Cling. Под linux труда это не составит (здесь ссылка на билд и нструкции), для отстройки под Windows читайте абзац ниже. Если вы не хотите утруждать себя билдом проектов, есть вариант скачать уже готовые бинарники для разных *nix систем. Итак, качаем, настраиваем пути и готовимся посмотреть, что же из себя представляет Cling.
Перед продолжением я хотел бы сделать несколько замечаний по билду проектов LLVM/Clang/Cling. Что касается LLVM и Clang, то их сбилдить не составит труда под Windows. Однако, с Cling на текущий момент (на момент написания поста) ситуация не так проста. Проект можно попробовать отстроить (у меня во всяком случае билд прошел успешно), однако работоспособность интерпретатора оставляет желать лучшего - постоянные исключения и ошибки. К сожалению, проект для Windows еще сыроват. Для получения дополнительно информации, советую посетить официальную ветку форума посвященную поддержки Cling (Cling Support), хотя тем там не очень много (кстати, одна из них - моя и посвящена как раз проблеме билда под Visual Studio 2010).
Не лишним будет почитать комментарии на странице описания интерпретатора.
Для желающих таки попробовать свои силы, следующий раздел посвящен билду под Windows. Я же работать с интерпретатором буду под CentOS.
На блоге “Solarian Programmer” есть статья по билду Cling под Windows с использованием Cygwin'а — “Building Cling (the C++ interpreter) on Windows”.
Руководство по постройке проекта Clang под Windows можно найти на сайте проекта clang.llvm.org.
Помимо Visual Studio, нам понадобятся следующее:
Скопируем проект LLVM
Загрузим проект Clang
Загрузим проект Cling
Пропатчим Cling. На данном этапе советую прочитать секцию “Building cling within LLVM and clang instead of ROOT” на официальной странице по билду проекта
Сгенерируем проект для Visual Studio с помощью cmake. Так же можно почитать о возможных конфигурационных опциях cmake в LLVM CMake guide.
Отстроим решение
После билда в соответствующей директории появятся бинарники.
Для лучшего понимания что есть Cling есть неплохое видео на английском от создателей о возможностях интерпретатора, его внутренней механике, а также о предпосылках его создания: страница “What is Cling”, раздел “Resources”.
Для получение справки об опциях Cling'а используйте классический вариант с ‘–help’. Для получения информации о командах, доступных при запущенном интерпретаторе, используйте команду ‘.help’.
Возможности интерпретатора весьма широки чтобы использовать его для разного рода обычных вычислений. Тем не менее, рассмотрим пример:
[cling]$ char symbol = '1';
[cling]$ symbol
(char) "1"
[cling]$ symbol + 5
(int const) 54
[cling]$
С помощью Cling'а можно наглядно увидеть сюрпризы, возникающие при неявном приведении типов:
[cling]$ float remain = 3/8;
[cling]$ remain
(float) 0.000000e+00
[cling]$ remain = (float)3/8
(float) 3.750000e-01
[cling]$
В случае ошибки, вывод довольно информативен:
[cling]$ #include <ioostream>
input_line_10:1:10: fatal error: 'ioostream' file not found
#include <ioostream>
^
[cling]$ #include <iostream>
[cling]$
На данный момент похоже функция выгрузки (команда “.U source”) просто не работает. По крайней мере, на CentOS мне постоянно выпадала Segmentation Fault. Попробуйте, возможно у вас все получится. Ниже пример-образец, как должна на самом деле работать функция выгрузки.
Допустим, есть версия файла wlevel.h со структурой WLevel, содержащей функцию возврата верхнего лимита при тревоге типа Warning. Допустим в первой версии структуры возвращаемое значение типа float, а в новой - типа int. Вот как можно с этим работать в Cling:
Файлы:
// wlevel.h, v.1
typedef struct _WARNING_LEVEL
{
float GetUpperBound() { return 60.5; }
} WLevel;
// wlevel.h, v.2
typedef struct _WARNING_LEVEL
{
int GetUpperBound() { return 60; }
} WLevel;
Терминал:
[gahcep@gahcep-vm bin]$ ./cling
****************** CLING ******************
* Type C++ code and press enter to run it *
* Type .q to exit *
*******************************************
[cling]$ .L wlevel.h
[cling]$ WLevel warning;
[cling]$ warning.GetUpperBound()
(float const) 6.050000e+01
[cling]$ .U wlevel.h
// Меняем файл wlevel.h
[cling]$ .L wlevel.h
[cling]$ WLevel warning;
[cling]$ warning.GetUpperBound()
(int const) 60
[cling]$
Функции создаются с помощью команды .rawInput, обрамляющей начало и конец тела функции:
[cling]$ #include <iostream>
[cling]$ .rawInput
Using raw input
[cling]! int foo(int x, int y) {
[cling]! ? std::cout << "X=" << x << std::endl;
[cling]! ? std::cout << "Y=" << y << std::endl;
[cling]! ? return x + y;
[cling]! ? }
[cling]! .rawInput
Not using raw input
[cling]$ foo(3,10)
X=3
Y=10
(int const) 13
[cling]$
Для того, чтобы были доступны функциональность последнего стандарта C++, интерпретатор должен быть вызван с опцией “-std=c++11”. Рассмотрим использование “auto” и “lambda”.
[gahcep@gahcep-vm bin]$ ./cling -std=c++11
[cling]$ #include <iostream>
[cling]$ using namespace std;
[cling]$ int cnt = 5;
[cling]$ auto proc = [&] (unsigned val) -> void { while (cnt-- > 0) cout << val << endl; }
(class <lambda at input_line_7:2:14>) @0x4a9011
[cling]$ proc(77)
77
77
77
77
77
[cling]$
Давайте создадим класс и тут же попробуем его использовать:
[cling]$ class Foo {
[cling]$ ? int state;
[cling]$ ? public:
[cling]$ ? Foo() { state = 0; };
[cling]$ ? void SetValue(int value);
[cling]$ ? int GetValue();
[cling]$ ? };
Для реализации методов класса необходимо использовать .rawInput:
[cling]$ .rawInput
Using raw input
[cling]! void Foo::SetValue(int value) {
[cling]! ? state = value;
[cling]! ? }
[cling]! int Foo::GetValue() {
[cling]! ? return state;
[cling]! ? }
[cling]! .rawInput
Используем класс:
[cling]$ Foo f
(class Foo) @0x7d5010
[cling]$ f.SetValue(777);
[cling]$ f.GetValue()
(int const) 777
[cling]$
Реализовать конструктор класса наравне с его методами у меня не получилось из-за ошибки Cling'а (sic!). Возможно дело в моей среде либо некорректных бинарниках.
Еще одной из возможностей интерпретатора является выполнение функции из файла при условии что файл содержит функцию возвращающую типа void. Кроме того, имя файла и функции должны быть идентичны.
Файл Bar.c:
void Bar(int x, int y, string str)
{
cout << str << "; X=" << x << "; Y=" << y << endl;
}
Вызовем фунцию:
[cling]$ #include <iostream>
[cling]$ using namespace std;
[cling]$ .x Bar.c(5,234,"Arguments")
Arguments; X=5; Y=234
[cling]$
На этом примере я закончу, однако возможности Cling'а далеко не исчерпаны. Читайте документацию, смотрите видео, экспериментируйте.
« Previous Blog Post | Back to top | Next Blog Post »