Практика мультипарадигмального программирования, 03 лекция (от 12 марта)

Материал из eSyr's wiki.

Перейти к: навигация, поиск

Предыдущая лекция | Следующая лекция

Tcl (продолжение)

Это хороший пример языка, который встраивается, и в него можно встроить. В прошлый раз был рассмотрен пример встраивания Tcl в C-шное приложение. В этот раз рассмотрим одну интересную C-шную функцию (в прошлый раз были Tcl_CreateInterp, Tcl_Eval, Tcl_GetStringResult): ... . Нужно было компилировать с библиотекой, компоновать... Но эта библиотека не ограничена этими тремя функциями. Рассмотрим новые функции:

  • Tcl_CreateCommand(Tcl_Interp * interp, char * cmdName, cmdProc * proc, 0, 0 )
    • interp — интерпретатор, в котором хотим создать команду
    • cmdName — имя команды
    • proc — указатель на функцию, которая возвращает инт, получает ClientData, интерпретатор и argc, argv

Два последних параметра не обязательно нули, один из них вроде бы ClientData, но обычно ставят туда именно нули.

typedef int (* cmdProc)(ClientData, Tcl_Interp *, int /* argc */, const char ** /* argv */);

Позволяет встроить в Tcl команду, написанную на Си или Си++. Так обычно и делают: создают интерпретатор, погружают в него проблемно-ориентированные вещи, так как просто интерпретатор неинтересен. Мало того, можно сделать интерпретатор главной программой. То есть программа написана на Tcl и дополнена функциями, которые загнаны в библиотеку, а в интерпретаторе скажем load. Более того, можно расширить уже встроенный интерпретатор.

Если вы хотите запретить load, то надо не бросаться искать, как её запретить, а разобраться, действительно ли вы этого хотите, потому что я уверен, что вы этого не хотите.

Пример. Функция, которая делает реверс слова

reverse: abcde → edcba

#include <tcl.h>
int ReverseString(ClientData ignored, Tcl_Interp * interp, int argc, char * argv[])
{
  int len = 0;
  char * result = 0;
  int i = 0;

  if (argc != 2)
  {
    Tcl_SetResult(interp, "sampleReverseString", TCL_STATIC);
    return TCL_ERROR;
     /* TCL_STATIC — результат есть статическая константа, 
      * то есть копировать и удалять её не надо, мы гарантируем, 
      * что эта строка никуда не денется и можно просто поставить 
      * на неё указатель */
  }

  len = strlen(argv[1]);
  res = (char *)malloc(len + 1);
  for (i = 0; i < len; i++)
  {
    res[i] = argv[1][len – 1 – i];
  }
  res[len] = 0;
  Tcl_SetResult(interp, res, TCL_VOLATILE);
  /* TCL_VOLATILE — вот тебе строка, сними с неё копию прямо сейчас */
  free(res);
  return TCL_OK;
}

Эту функцию можно встроить в любой интерпретатор Tcl с помощью Tcl_CreateCommand. Как это встроить в отдельно стоящий интерпретатор: общая идея в следующем: мы должны создать разделяемую библиотеку (tcl_sample.so), в которой будет несколько функций, которые прилинкованы как static, и одна нестатическая, которая будет называться аналогично имени библиотеки:

Tcl_sample_Init
/* Библиотека может подгружать несколько функций, ибо делать 
 * библиотеку ради одной функции обычно смысла не имеет */
int Tcl_sample_Init(Tcl_Interp * interp)
{
  struct {
    char * cmdname;
    cmdProc cmdp;
  } funcs[] = {
    { "sampleReverseString". sampleReverseString } 
     ...
     {0, 0}
  };

  int i = 0;

  for (i = 0; funcs[i].cmdp; i++)
  {
    Tcl_CreateCommand(interp, funcs[i].cmdname, funcs[i].cmdp, 0, 0);
  }
}

Что такое mangling — в С++ есть перегрузка функций. Там может быть много функций с одинаковым именем и разными типами. Загрузчик этого не умеет, поэтому компилятор вынужден информацию о типах сохранять в имени функции. Но Tcl ищет конкретную функцию. Добиться неприменения mangling просто:

#ifdef _cplusplus
extern "C"
{
  int Tcl_sample_Init(Tcl_Interp *);
}
#endif

В самом интерпретаторе:

load "tcl_sample.so"

Где это нужно: например, в Tcl/Tk (Tk — Toolkit, набор виджетов)

Есть wish. Что он собой представляет: Сишное приложение с встроенным интерпретатором тикля, каковой интерпретатор расширен командами тикля для работы с графическими элементами.

Пример на Tcl/Tk: небольшое приложение, которое будет собой представлять 4 элемента диалогового окна — 3 кнопки и лэйбл. Есть главное окно, которое обозначается точкой. Под ним будут дочерние окна:

  • .lab
  • .buttons.left
  • .buttons.right
  • .quit

почему это происходит быстро? В wish есть возможность автоматически рапролагать новые элементы. Изначально есть пустое окно. Новые элементы можно паковать (pack) на форме — прижимать их к краю окна. В Windows же нужно использовать файлы ресурсов, и т. д. Здесь таких проблем нет вообще. wish может работать с ncurses, в иксах, и т. д.

#!/usr/bin/wish

Нехорошо, так как в разных системах лежит в разных местах.

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

#!/bin/sh
#\
export PATH=/opt/bin:$PATH
exec wish "$0" -name mydemo "$@"
Команды Tk:
  • label
  • frame
  • button
  • pack
  • wm
set w "" ;#root window
label $w.lab -text {Just a label}
pack $w.lab -side top
frame $w.buttons
button $w.buttons.left -text Left -command
{
  $w.lab configure -text {Left pressed}
}
button $w.buttons.right -text Right -command
{
  $w.lab configure -text {Right pressed}
}
pack $w.buttons.left $w.buttons.right -side left
pack $w.buttons -side top
button $w.quit -text {Quit program} -command exit
wm title . {This is a demo }

Здесь есть аналоги виндовых файлов ресурсов, но предназначены они немного для другого. Например, мы хотим сделать приложение, в котором можно менять язык. С символами нацкодировок всё, в принципе, неплохо. Но есть Юникод. Это в принципе плохо, но не так, как если бы пришлось вставлять это в код программы. Как это сделать:

  • Выкидываем все -text
  • Добавляем файл опций sample.rc
set w "" ;#root window
option readfile "sample.rc"
...

(sample.rc) 
mydemo.lab.text: Just a label
mydemo.buttons.left.text : Left Button
mydemo.buttons.right.text : Right Button
mydemo.quit.text : Quit Program

Как компилировать .so

Тайное знание: в расширяемых библиотеках код должен обладать свойством переносимости, то есть код должен быть с относительными адресами. Всё, что нужно сказать компилятору — компилировать сошку с определёнными ключами. Для С:

  • Из файла объектный файла
gcc -Wall -g -fpic -c tcl_sample.c
  • Как собрать библиотеку
gcc -shared tcl_sample.o ... -o tcl_sample.so


Практика мультипарадигмального программирования


01 02 03 04 05 06 07 08


Календарь

пн пн пн пн пн
Февраль
12 19
Март
12 19 26
Апрель
02 09 16


Эта статья является конспектом лекции.

Эта статья ещё не вычитана. Пожалуйста, вычитайте её и исправьте ошибки, если они есть.
Личные инструменты
Разделы