<ctpp/> главная .:. скачать .:. документация .:. faq .:. разработчикам  
o Главная
    Скачать!
    Установка

o Помощь
    Что такое CTPP?
    Как работает CTPP?
    Онлайн-документация
    FAQ

o В действии
    Проекты
    Первые шаги
    Как сделать?..
    Разработчикам

o В разработке
    Тесты производительности
    Расписание
    Разработчики

    Благодарности




Как написать собственную функцию в CTPP2?

Введение

В API шаблонизатора CTPP2 реализована возможность подключения пользовательских функций. Пользовательские функции могут значительно повысить производительность шаблонизатора и практически неограниченно расширить его функционал. Этот документ расскажет вам, как самостоятельно написать такое расширение.

Разрабатываемая в этом примере функция будет форматировать дату, переданную ей в виде unixtime.

Базовым классом для всех пользовательских функций является класс SyscallHandler из пространства имен CTPP. Класс описан в VMSyscall.hpp, поэтому нам придется подключить этот заголовочный файл. Класс содержит в себе 6 виртуальных методов: InitHandler, PreExecuteSetup, Handler, GetName, GetVersion, DestroyHandler.

Предназначение методов следующее:
Метод InitHandler используется для инициализации обработчика пользователем. Метод имеет ровно один параметр типа CDT. Если в инициализации нет необходимости, метод реализовывать необязательно.

/*
  Глобальная инициализация:
  oCDT - параметр инициализации
  Метод возвращает 0, если выполнился успешно и -1 при возникновении ошибки
*/
virtual INT_32 InitHandler(CDT & oCDT);

Метод PreExecuteSetup вызывается непосредственно перед запуском байткода в виртуальной машине и нужен для передачи функции необходимых для работы данных. Если функция не работает с сегментами данных, функций или глобальными параметрами, реализация метода необязательна.

/*
  Передача необходимых для работы данных
  oCollector - коллектор вывода данных
  oCDT - переданные в CTPP параметры
  oSyscalls - сегмент системных вызовов
  oStaticData - сегмент статических данных
  oStaticText - сегмент статического текста
  Метод возвращает 0, если выполнился успешно и -1 при возникновении ошибки
*/
virtual INT_32 PreExecuteSetup(OutputCollector          & oCollector,
                               CDT                      & oCDT,
                               const ReducedStaticText  & oSyscalls,
                               const ReducedStaticData  & oStaticData,
                               const ReducedStaticText  & oStaticText);

Метод Handler является "рабочим телом" пользовательской функции и вызывается каждый раз, когда требуется выполнить функцию. Метод содержит три параметра: массив аргументов, их количество и возвращаемое значение, передаваемое по ссылке.ВАЖНО: аргументы задаются в обратном порядке, поэтому при вызове FOO_BAR(a, b, c), в параметры в массиве аргументов будут переданы как aArguments[0] -> c, aArguments[1] -> b, aArguments[2] -> a.

/*
  Обработчик
  aArguments - массив аргументов функции
  iArgNum - число аргументов
  oCDTRetVal - возвращаемое функцией значение
  Метод возвращает 0, если выполнился успешно и -1 при возникновении ошибки
*/
virtual INT_32 Handler(CDT           * aArguments,
                       const UINT_32 & iArgNum,
                       CDT           & oCDTRetVal) = 0;

Важный момент: Перед запуском метода Handler в переменной oCDTRetVal находится мусор, поэтому, если функции требуется вернуть результат исполнения, необходимо указать тип переменной. Это можно сделать как явно, написав, к примеру, oCDTRetVal = CDT(CDT::INT_VAL);, так и неявно: oCDTRetVal = 10;

Метод GetName возвращает имя функции (asciz-строку).

/*
  Получить имя функции.
*/
virtual CCHAR_P GetName() = 0;

Метод GetVersion возвращает версию API; текущая версия - 2. В случае изменения API хранимых функций, знание версии позволит выбирать правильный механизм запуска методов.

В случае, если в методе InitHandler требуется выделить для работы память, подключиться к БД, или выподнить подобные действия, в методе DestroyHandler возможно освободить выделенную память, закрыть подключение к БД и т.д.

/*
  Handler resources destructor
  oCDT - данные, использованные при инициализации
  Метод возвращает 0, если выполнился успешно и -1 при возникновении ошибки
*/
virtual INT_32 DestroyHandler(CDT & oCDT);

Собственная функция

Поскольку разрабатываемая функция не обращается ни к данным, ни к стеку, нам необходимо реализовать всего два метода: Handler и GetName. Соответственно, файл заголовков будет выглядеть так: FnDateFormat.hpp

#ifndef _FN_DATE_FORMAT_HPP__
#define _FN_DATE_FORMAT_HPP__ 1

// Подключаем определение базового класса
#include <VMSyscall.hpp>

namespace MY_NS
{
// Наш обработчик
class FnDateFormat:
  public CTPP::SyscallHandler
{
public:
	// Деструктор
	~FnDateFormat() throw();

private:
	// Обработчик
	INT_32 Handler(CDT           * aArguments,
                       const UINT_32 & iArgNum,
                       CDT           & oCDTRetVal);

	// Имя функции
	CCHAR_P GetName();
};

} // namespace MY_NS
#endif // _FN_DATE_FORMAT_HPP__
// End.

Файл реализации FnDateFormat.сpp:

// Подключаем определение нашего класса
#include <FnDateFormat.hpp>

// Необходимо для strftime
#include <time.h>

namespace MY_NS
{

// Деструктор
FnDateFormat::~FnDateFormat() throw() { ;; }

// Обработчик
INT_32 FnDateFormat::Handler(CDT           * aArguments,
                             const UINT_32 & iArgNum,
                             CDT           & oCDTRetVal)
{
	// Наша функция принимает только два агрумента
	if (iArgNum != 2) { return -1; }

	// Временный буфер
	CHAR_8 szBuffer[1024 + 1];

	// Получаем значение времени из первого аргумента (помним про обратный порядок)
	time_t iTime = aArguments[1].GetInt();
	const struct tm * pTM = localtime(&iTime);

	// Форматируем время, как указано во втором аргументе
	if (strftime(szBuffer, 1024,
	       aArguments[0].GetString().c_str(), pTM) == 0) { return -1; }

	// Возвращаем значение из функции
	oCDTRetVal = szBuffer;

return 0;
}

// Имя функции
CCHAR_P FnDateFormat::GetName() { return "date_format"; }

} // namespace MY_NS
// End.

Шаблон для вызова функции:


Date: <TMPL_var DATE_FORMAT(1200490323, "%Y-%m-%d %H:%M:%S")>

Проверка работы

Заходим в каталог examples/extra_fn дистрибутива CTPP2, компилируем проект:
cmake . && make

Компилируем шаблон:
ctpp2c date_format.tmpl date_format.ct2

Проверяем работу:
./date_test date_format.ct2
После запуска виртуальная машина выведет


Date: 2008-01-16 16:32:03


Copyright © 2003 - 2008 CTPP Dev Team.