Google

Система расширения

Практически 90% функциональных возможностей CLIP, сделаны на базе его системы расширения.
Система расширения представляет собой набор функций для получения параметров при вызове, возврат данных, генерация ошибок, некоторые функции для обработки массивов, объектов и других типов в представлении на языке С/С++.
Например простейшая функция будет представляться в С-сырце примерно так:

#include "clip.h"
#include "error.ch" // ну еще тут надо stdlib, stdio, .....

int
clip_SUBSTR(ClipMachine * mp)
// Название подключаемой функции должно начинаться с "clip_" и далее
// название CLIPPER-функции в верхнем регистре.
// такие имена clip-компилятор помещает в специальный список доступных
// в run-time функций.
// mp - это описание текущего состояния clip-машины, лучше смотрите ее структуру
// непосредственно в clip.h
// Если эта функция возвращает 0 - то ошибок не произошло, если
// не 0 - то это будет код ошибки.
{
	int l;
	char *ret;
	char *str = _clip_parcl(mp, 1, &l);
// получаем первый параметр в виде строки с конкретной длиной.
	int from = _clip_parni(mp, 2);
	int kol = _clip_parni(mp, 3);
// получаем второй и третий параметр в виде числа с автоматическим преобразованием
// в int - тип.
// если передаваемые параметры были не того типа - в качестве С-значений
// будет получено NULL
	if (str == NULL)
	{
		_clip_retc(mp, "");
		return _clip_trap_err(mp, EG_ARG, 0, 0, __FILE__, __LINE__, "SUBSTR");
// проверка полученной строки и генерация ошибки в случае неправильно
// переданного параметра
	}
// собственно алгоритм обработки
// на него можно не обращать внимания
	if (kol <= 0)
		kol = 0;
	if (from <= 0)
	{
		from = l+from;
		if (from<0)    	from=0;
	}
	else
		from--;
	if (_clip_parinfo(mp, 0) < 3)
		kol = l - from;
	if ((kol + from) > l)
		kol = l - from;
	if (kol < 0)
		kol = 0;
	ret = malloc(kol + 1);
	memcpy(ret, str + from, kol);
	ret[kol] = 0;
	_clip_retcn_m(mp, ret, kol);
// возврат значения в clip-машину в виде строки (с выделенной областью памяти)
// с указанной длиной (потому что в clipper-строках могут быть нулевые
// байты!).
// если ничего не вернуть посредством _clip_.... , то в clip-машину вернется NIL
	return 0;
// сообщение clip-машине что функция завершилась без ошибок
}
Ну а теперь собственно коротко о тех функциях расширения, которыми надо пользоваться при написании новых возможностей для clip. Для тех, кто легко читает и пишет на Си - лучше загляните в файл clip.h.

Функции получения передаваемых параметров

int _clip_parinfo(ClipMachine * mp, int num)

Если num==0 - выдает количество параметров, если не 0 - выдает тип переданного параметра с номером num.

int _clip_parni(ClipMachine * mp, int num)

Возвращает значение параметра с номером num преобразованное к типу int

long _clip_parnl(ClipMachine * mp, int num)

Возвращает значение параметра с номером num преобразованное к типу long

double _clip_parnd(ClipMachine * mp, int num)

Возвращает значение параметра с номером num преобразованное к типу double

int _clip_parp(ClipMachine * mp, int num, int *len, int *dec)

Заполняет информацию о параметре с номером num - длину и точность представления

char *_clip_parc(ClipMachine * mp, int num)

Возвращает значение параметра с номером num как строку без длины

char *_clip_parcl(ClipMachine * mp, int num, int *len)

Возвращает значение параметра с номером num как строку с длиной (в clipper-строках могут быть нулевые байты!)

int _clip_parl(ClipMachine * mp, int num)

Возвращает логическое значение параметра с номером num в виде 0/1

ClipVar *_clip_par(ClipMachine * mp, int num)

Думаю, это и так понятно, а кому не понятно лучше не трогать.

long _clip_pardj(ClipMachine * mp, int num)

Возвращает значение параметра с номером num, как дату преобразованную в julian - представление

long _clip_pardc(ClipMachine * mp, int num, int *yy, int *mm, int *dd, int *ww)

Возвращает значение параметра с номером num как дату представленную в виде year,month,day, millenium

Функции возврата данных в clip-машину

void _clip_retni(ClipMachine * mp, int n)

Вернуть число, преобразовав его из int.

void _clip_retnl(ClipMachine * mp, long n)

Вернуть число, преобразовав его из long.

void _clip_retnd(ClipMachine * mp, double n)

Вернуть число, преобразовав его из double.

void _clip_retndp(ClipMachine * mp, double n, int len, int dec)

Вернуть число, преобразовав его из double в формате с длиной len и точностью dec.

void _clip_retc(ClipMachine * mp, char *str)

Вернуть строку, предварительно скопировав данные из str. Str должна быть освобождена без участия clip-машины!

void _clip_retcn(ClipMachine * mp, char *str, int len)

Вернуть строку c длиной len, предварительно скопировав данные из str. Str должна быть освобождена без участия clip-машины!

void _clip_retcn_m(ClipMachine * mp, char *str, int len)

Вернуть строку c длиной len, без копирования данных из str. Str будет освобождена clip-машиной когда эта строка не будет больше использоваться (когда кол-во ссылок на эту строку станет равно 0).

void _clip_retl(ClipMachine * mp, int l)

Вернуть логическое значение.

void _clip_retdj(ClipMachine * mp, long julian)

Вернуть дату, преобразовав из формата julian.

void _clip_retdc(ClipMachine * mp, int yy, int mm, int dd)

Вернуть дату, преобразовав из формата year, month, day.

void _clip_retnr(ClipMachine * mp, struct rational *r, int len, int dec)

Вернуть рациональное число в формате с длиной len и точностью dec.

Функции вычисления hash-кодов

hash-коды используются в clip-машине на каждом углу, так что частенько их приходиться вычислять. Описывать особенно их собственно и нечего - и так понятно по параметрам.

long _clip_hashstr(const char *x)

long _clip_casehashstr(const char *x)

long _clip_hashbytes(long seed, const char *bytes, int len)

long _clip_casehashbytes(long seed, const char *bytes, int len)

long _clip_hash(ClipMachine * mp, ClipVar * vp)

long _clip_casehash(ClipMachine * mp, ClipVar * vp)

Функции генерации run-time ошибок

Для более полного понимания сущности "ошибка" читайте о классе error и тогда все эти функции вам станут понятными.

void _clip_trap(ClipMachine * mp, const char *filename, int line)

void _clip_trap_str(ClipMachine * mp, const char *filename, int line, const char *str)

void _clip_trap_printf(ClipMachine * mp, const char *filename, int line, const char *fmt,...)

void _clip_trap_printv(ClipMachine * mp, const char *filename, int line, const char *fmt, void *vect)

void _clip_trap_var(ClipMachine * mp, const char *filename, int line, ClipVar * var)

void _clip_trap_pop(ClipMachine * mp)

void _clip_trap_invargv(ClipMachine * mp, const char *filename, int line)

int _clip_trap_err(ClipMachine * mp, int genCode, int canDefault, int canRetry, const char *subSystem, int subCode, const char *operation)

Функции управления статическими данными

Если появиться необходимость хранить какие-то статические данные (например какой-нибудь свой set(_MY_SET_1,MY_DATA) ), то в clip-машине существует небольшой набор функций для этого. Внимание! Явно в языке Си объявлять конструкции типа "static my_type var_name" категорически запрещено. Такие данные могут привести в конфликту между несколькими clip-процессами!

void _clip_store_item(ClipMachine * mp, long hash, void *item)

Запомнить данные item под идентификатором hash (для его генерации лучше всего использовать функции *_hash_*). Область памяти под item должна быть явно выделена!

void _clip_store_item_destroy(ClipMachine * mp, long hash, void *item, void (*destroy) (void *))

Установить функцию освобождения памяти для статических данных с идентификатором hash.

void _clip_free_item(ClipMachine * mp, long hash)

Освободить память для идентификатора hash.

void _clip_remove_item(ClipMachine * mp, long hash)

Вообще уничтожить всю информацию о hash.

void *_clip_fetch_item(ClipMachine * mp, long hash)

Возвращает указатель на область памяти с идентификатором hash.

Функции управления контейнерами

Контейнер - это еще одно средство для хранения статических данных. Например, в clip-машину трудно вернуть какой-нибудь Си-указатель на структуру.
например:
FILE * fh
fh=open(filename,...)
Куда девать указатель "FILE * fh"?
А ведь в clipper-функции fopen() возвращаемое значение - простое число.
Или, например, структура открытого соединения с ORACLE,MYSQL и т.п.
Тоже ведь желательно внутри clip-программы лучше все-таки оперировать числом.
Вот именно для хранения таких структур и предназначен контейнер.

void *_clip_fetch_c_item( ClipMachine *cm, int key, int type )

int _clip_destroy_c_item( ClipMachine *cm, int key, int type )

int _clip_store_c_item( ClipMachine *cm, void *item, int type, void (*destroy)(void*) )

В данных функциях:
key - номер файла,соединения и т.п.
type - тип хранимой информации (это для того, чтобы случайно не подсунули
номер файла вместо номера соединения).
*destroy - функция уничтожения/закрытия хранимой структуры.

Другие возможности

Вышеперечисленными функциями C-API не ограничивается. Есть еще масса функций по управлению массивами, объектами, вводом-выводом, вызовами кодовых блоков и многое другое. Но данная информация будет предоставляться только тем, кто имеет соответствующую квалификацию, так как некорректное, бестактное и неквалифицированное использование данных возможностей может привести к грубым и непонятным "падениям" прикладных программ, утечке памяти, и как результат - наезды на нас. Кто захочет прикрутить что-то серьезное к clip - обращайтесь - дадим и информацию и примеры, поможем в работе и освоению внутреннего устройства clip-машины.

Создание динамически загружаемых модулей для clip.

В clip имеется функция load(), которая загружает динамические модули (*.so) или байт-код (*.po) и библиотеки байт-кодов (*.pa).
Байт-код и so-модули легко получить из prg-файлов командами:
clip -p module.prg -> module.po
clip -s module.prg -> module.c -> module.so
А вот написать на Си такой же загружаемый модуль можно таким способом:
Создается module.prg и в нем описываются все необходимые пустые функции
function my_func1()
return
function my_func2()
return
затем этот модуль компилируется
clip -s module.prg
в результате получается Сишный текст в файле module.c.
Этот текст имеет заготовки для описанных функций my_func1,my_func2,.... Остается просто поменять тело функций по вышеописанным правилам для C-API и получится so-модуль написанный на Си. Далее его останется только скомпилировать Си-компилятором с ключиком -shared или аналогичным, в зависимости от компилятора.
© Ю.Хныкин, uri@itk.ru, 2000