воскресенье, 27 января 2008 г.

Серые будни.

Несколько месяцев назад я устроился на новую, престижную работу, в настоящую IT-компанию. В принципе все здорово, новый офис, хорошие люди, вид на Бештау из окна... и перспектива зарабатывать столько-же сколько зарабатывают люди моей профессии в Москве и Санкт-Петербурге $_$. Но как оказалось, у медали есть и обратная сторона. Теперь у меня пропало ощущение причастности к результату, так как то-что я делаю — всего-лишь малая часть целого. Все-бы ничего, вот только раньше у меня это ощущение было (я точно помню;), и именно оно давало стимул приходить на работу, и стараться что то делать... Сейчас-же чуствую себя винтиком. А это очень неприятное чувство, к нему конечно можно привыкнуть но страшно не хочется. Масла в огонь подлил один тип в ЖЖ. Товарисч что-то там админит за бугром, и проводит на работе по 14 часов в сутки, только потому что в его жизни почти ничего не осталось, кроме работы. Видимо так и становятся трудоголиками, время-то надо куда-то девать. И естественно возникает вопрос, на кой черт вообще работать, если это не нравится? Что-бы заработать денег, потом их просрать (превед пятниЦЦо!!!) , и потом опять на работу, что-бы... что-то в этом лишнее, видимо работа :)) Она ведь нужна только для того, что-бы было на что вести нормальный образ жизни, а то получится как в филме Fight Club :-D. В общем, превращаться в офисный планктон и быть хозяином своей жизни это разные вещи...

пятница, 25 января 2008 г.

Метапрограммирование на С++.

Сегодня я закончил проект в котором очень плотно использовал технику программирования, в народе известную как Generic Metaprogramming %) Особенность данной методики состоит в том, что программист не пишет алгоритм программы, а создает его обобщенную реализаецию. Потом по обобщенной реализации компилятор генерирует код... Приемуществ у этого подхода масса, главное состоит в том что большинство ошибок отсеивается на этапе компиляции. Плюс сам процесс разработки становится нелинейным, сначала работа продвигается как обычно, потом, когда написан обобщенный код, продуктивность резко возрастает, в частности я за сегодня написал то - что планировал писать еще одну неделю минимум :). Причина этого в том что мне не пришлось дублировать похожие участки кода в разных местах... вот такой вот бонус))

четверг, 24 января 2008 г.

Долой switch!!!

Незнаю у кого как, но у меня, при виде оператора switch на 2 страницы, появляются подозрения, что, что-то здесь не так... По сути программист не должен в ручную прописывать все возможные варианты работы кода, за него это должен делать компилятор. В правильно написанной программе (на C++) роль switch чаще всего выполняет таблица виртуальных функций. Но ведь не будешь на каждую мелочь создавать иерархию объектов. В этом случае могут помочь шаблоны... Мне раньше часто приходилось писать уродливый код вроде этого:
int _tmain(int argc, _TCHAR* argv[])
{
    unsigned long value = 123465;
    unsigned char buffer[0x100];
    if ( value <= numeric_limits<unsigned char>::max() )
    {
        unsigned char* ptr = reinterpret_cast<unsigned char*>(buffer);
        *ptr = (unsigned char)value;
    } else if ( value <= numeric_limits<unsigned short>::max() ) {
        unsigned short* ptr = reinterpret_cast<unsigned short*>(buffer);
        *ptr = (unsigned short)value;
    } else if ( value <= numeric_limits<unsigned int>::max() ) {
        unsigned int* ptr = reinterpret_cast<unsigned int*>(buffer);
        *ptr = (unsigned int)value;
    }
    return 0;
}
здесь переменная value записывается в массив, причем в зависимости от ее значения, под ее хранение отводится 1, 2, или 4 байта... код для разных value отличается только типами значений, в остальном он идентичен. Может показаться что проблема притянута за уши, но это не так - это очень серьезная проблема)) Что если вариантов не 3, а скажем 10, а если потребуется добавить еще один? А что если потом потребуется изменить алгоритм ;) Я нашел решение этой проблемы в использовании списков типов. Вот мой вариант такого спска:
class null_t;
template <typename Head, typename Tail>
struct type_list_node_t
{
    typedef Head Type;
    typedef Tail Next;
};
а вот пример использования:
typedef type_list_node_t<unsigned char, 
        type_list_node_t<unsigned short,
            type_list_node_t<unsigned int, null_t> 
        > 
    > my_typelist;
в принципе все очень просто, type_list_node_t - это элемент списка, который может содержать еще один type_list_node_t и т.д. класс null_t служит маркером окончания списка... подробнее можно почитать у Александресску... В общем, не вдаваясь в подробности - следующий код занимается генерацией всех возможных ветвлений, для заданного набора типов, используя для этого рекурсию. Компилятор может это оптимизировать так, что по скорости выполнения, разницы, по сравнению с первым вариантом, не будет.
template<typename T>
struct limits_helper_t;

template<typename Head, typename Tail>
struct limits_helper_t< type_list_node_t<Head, Tail> >
{
    bool operator () (unsigned long value, unsigned char* buffer)
    {
        if ( value <= numeric_limits<Head>::max() )
        {
            Head* ptr = reinterpret_cast<Head*>(buffer);
            *ptr = (Head)value;
            return true;
        }
        limits_helper_t <Tail> next;
        return next(value, buffer);
    }
};

template<>
struct limits_helper_t< null_t >
{
    bool operator () (unsigned long value, unsigned char* buffer)
    {
        return false;
    }
};


int _tmain(int argc, _TCHAR* argv[])
{
    unsigned long value = 123465;
    unsigned char buffer[0x100];
    typedef type_list_node_t<unsigned char, 
                        type_list_node_t<unsigned short,
                            type_list_node_t<unsigned int, null_t> 
                        > 
                    > my_typelist;
    limits_helper_t<my_typelist> helper;
    helper(value, buffer);
    return 0;
}
однако чем-то смахивает на функциональное программирование :-)

вторник, 22 января 2008 г.

Черный властелин

Сегодня прийдя на работу и открыв google reader был "приятно" удивлен, хабрахабр был под властью черного властелина :-D

понедельник, 21 января 2008 г.

Люблю препроцессор

Недавно наткнулся на забавную фишку в дебрях windows.h, я конечноже специально туда не лазил, просто захотел добавить к своему классу метод Yeild. Так вот, оказалось что гдето там в глубинах windows sdk объявлен макрос с таким-же именем ;). Непомню точно, но объявлен он вроде-бы так - #define Yeild, в общем жесть))

воскресенье, 20 января 2008 г.

Lua и C++, объекты.

Сейчас практически почти каждый язык программирования имеет встроенную поддержку ООП, С++ имеет, а Lua к сожалению нет :'( Точнее имеет, но несколько странную... Основная структура данных в языке lua - таблицы. В качестве ключей и значений таблицы, используются переменные, значения которых, типизируются динамически. Это означает что таблица может содержать пары, ключ - значение, разных типов. Таблица - единственная структура данных в lua. С помощью таблиц реализуются все остальные структуры данных, а так-же объекты. Например таблица, в качестве ключей которой используются численные значения, является аналогом одномерного массива. Таблица так-же может содержать функции.
--пример таблицы ... а это комментарий
local table = {}
table["pi"] = 3.14159
table[1] = "some string"
table[3.14159] = "pi"
Язык lua позволяет перегружать операции для таблиц и userdata, это делается с помощью мета-таблиц. Мета-таблица связывает события и их обработчики и представляет из себя обычную таблицу, в качестве ключей которой используются строки - имена событий, а в качестве значений - функции - обработчики событий. В свою очередь с каждой мета-таблицей, может быть связана еще одна мета-таблица, если событие не определено в первой мета-таблице, то оно ищется во второй, и так далее... События (ключи таблицы) - могут иметь как произвольные значения, так и предопределенные (всегда начинаются с "__"), например, "__add" - перегружает операцию сложения, "__gc" - вызывается перед удалением сборщиком мусора, ключ "__metatable" может содержать другую мета-таблицу и тд... Синтаксис языка предполагает использование мета-таблиц, для создания объектов, для этого предусмотрен оператор : (двоеточие). При вызове функции из таблицы, с помощью этого оператора, в качестве первого параметра будет неявно передана эта таблица. Теперь о том как это все работает. В языках программирования поддерживающих классы, объекты всегда создаются на основе информации о типах. В lua классов нет, но объекты все-таки можно создавать. Скрипт должен содержать код, для создания экземпляра объекта. Для этого он должен, создать новую таблицу и мета-таблицу, содержащую все методы объекта. Ниже приведен код, котрый позволяет использовать объекты класса PersonInfo, в lua скрипте. Для взаимодействия скрипта с объектами PersonInfo, предназначен класс PersonInfoBinder.
extern "C" {
#    include "lua.h"
#    include "lauxlib.h"
#    include "lualib.h"
}
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <conio.h>
class PersonInfo
{
    friend class PersonInfoBinder;
protected:
    std::string name;
    std::string sname;
    int age;
public:
    
    static int ObjCount;
    
    PersonInfo():
        name(),
        sname(),
        age(0)
    {
    }
    //конструктор    
    PersonInfo(const char* n, const char* sn, int a):
        name(n),
        sname(sn),
        age(a)
    {
        ObjCount++;
    }
    //деструктор
    virtual ~PersonInfo()
    {
        ObjCount--;
    }
    //метод который мы будем вызывать с помощью Lua
    virtual void ShowInfo()
    {
        printf("PersonInfo %s %s, age %d\n",name.c_str(), sname.c_str(), age);        
    }
};
int PersonInfo::ObjCount = 0;
//размещающий оператор new, для создания объекта внутри userdata 
void* operator new(size_t size, lua_State* L, const char* metatableName)
{    //создаем userdata на вершине стэка
    void* ptr = lua_newuserdata(L, size);
    /* состояние стэка
     * [-1] - объект userdata
     */
    //кладем на вершину стэка metatable с именем metatableName
    luaL_getmetatable(L, metatableName);
    /* состояние стэка
     * [-1] - metatable
     * [-2] - объект userdata
     */
    //привязываем metatable к объекту userdata
    lua_setmetatable(L, -2);
    return ptr;
}
//Класс через который интерпретатор Lua может работать с объектами PersonInfo
class PersonInfoBinder
{
public:
    typedef PersonInfo* pPersonInfo;
    
    //метод возвращает указатель на PersonInfo или NULL
    static pPersonInfo checkPersonInfo(lua_State* L, int ix)
    {    //если на вершине стэка объект userdata
        if (lua_isuserdata(L,ix))
        {    //возвращаем указатель на объект
            void* udata = luaL_checkudata(L,ix,"PersonInfo");
            if (udata != NULL) //если получится :-)
                return (pPersonInfo)udata;
        }
        return NULL;
    }
    
    //Ф-я вызывает метод ShowInfo объекта находящегося на вершине стэка
    static int Print(lua_State* L)
    {    //получаем указатель на объект
        pPersonInfo p = checkPersonInfo(L, 1);
        //и надеемся на удачу
        p->ShowInfo();
        return 0;
    }
    
    //метод создает объект PersonInfo
    static int New(lua_State* L)
    {    //получаем черезстэк параметры для создания
        const char* name = lua_tostring(L,1);
        const char* sname= lua_tostring(L,2);
        int age =  lua_tointeger(L,3);
        //создаем объект PersonInfo на вершине стэка
        pPersonInfo p = new (L, "PersonInfo") PersonInfo(name, sname, age);
        return 1;//сообщаем интерпретатору, что ф-я вернула один параметр
    }
    //Деструктор для объекта - функция вызываетс для объекта во время сборки мусора
    static int GC(lua_State* L)
    {    //единственный параметр - указатель на уничтожаемый объект
        pPersonInfo p = checkPersonInfo(L, 1);
        //вызываем деструктор явно, Lua самостоятельно освободит память занимаемую userdata
        p->~PersonInfo();
        return 0;
    }    
    //Ф-я делает объект типа PersonInfo доступным интерпретатору
    static int Bind(lua_State* L)
    {    //методы которые содержит метатаблица объекта
        static const luaL_Reg somebody_meta[] = {
          {"__gc",  &GC},
          {0, 0}
        };
        //методы самого объекта
        static const luaL_Reg somebody_methods[] = {
          {"New",  &New},
          {"Print", &Print},
          {0, 0}
        };
        //Ф-я создает глобальную переменную (таблицу) PersonInfo, и записывает в нее все методы из somebody_methods
        //данная таблица будет общей для всех объектов этого типа
        luaL_register(L,"PersonInfo",somebody_methods);
        //используем таблицу PersonInfo, в качестве мета-таблицы
        luaL_newmetatable(L, "PersonInfo");
        //создаем еще одну таблицу, на этот раз в стеке, содержащую методы из массива somebody_meta
        luaL_register(L,NULL,somebody_meta);
        //metatable.__index = methods
        lua_pushliteral(L, "__index");
        //копируем таблицу методов
        lua_pushvalue(L, -3);   
        lua_rawset(L, -3);
        /* metatable.__index = methods */
        lua_pushliteral(L, "__metatable");
        //копируем таблицу методов
        lua_pushvalue(L, -3);     
        lua_rawset(L, -3);  
        lua_pop(L, 1); 
        return 1;
    }
};
int
main(void)
{
    int status, result;
    double sum;
    lua_State *L;
    L = lua_open();
    
    luaL_openlibs(L);
    PersonInfoBinder::Bind(L);
    
    status = luaL_loadfile(L, "script.lua");
    if (status) {
        (void)fprintf(stderr, "Syntax error\n");
        getch();
        exit(1);
    }
    result = lua_pcall(L, 0, LUA_MULTRET, 0);
    if (result) {
        fprintf(stdout, "runtime error\n");
        getch();
        exit(1);
    }
    lua_close(L); 
    
    printf("Objects: %d", PersonInfo::ObjCount);
    getch();
    return 0;
}
Сценарий, который этот код выполняет:
-- script.lua
-- ввод значений пользователем
function read_record(table)
    io.write("first name: ")
    name = io.read()
    io.write("second name: ")
    sname = io.read()
    io.write("age: ")
    agestr = io.read()
    age = tonumber(agestr)
    local person = PersonInfo.New(name, sname, age)
    table[name..' '..sname] = person
end
-- вывод значений таблицы
function find_record(table)
    name = io.read()
    if table[name] ~= nil then
        table[name]:Print()
    else
        print("\a\r")
    end    
end
-- Удаление значений из таблицы
function remove_record(table)
    name = io.read()
    if table[name] ~= nil then
        table[name] = nil
    else
        print("\a\r")
    end    
end
-- пустая таблица
table = {}
command = ""
while command ~= "quit" do
    io.write(">> ")
    command = io.read()
    if command == "quit" then
        break
    elseif command == "find" then
        find_record(table)
    elseif command == "add" then
        read_record(table)
    elseif command == "remove" then
        remove_record(table)
    else
        print("\a\r")
    end
end
return 0

Lua и C++, функции.

Этот пост о том, как заставить интерпретатор lua вызывать функции написанные на С++. Чтобы интерпретатор мог вызвать функцию, программа, сперва, должна эту функцию зарегистрировать. lua_register(lua_State*, Имя функции, Адрес функции) Функция должна возвращать значение типа int и получать в качестве параметра указатель на lua_State:
int glue_function(lua_State* L);
Склеивающая функция должна следовать определенному протоколу для нормальной работы. В первую очередь она должна получить через lua_State количество переданных ей аргументов, это значение находится на вершине стека во время вызова.
int glue_function(lua_State* L);
Затем функция может обращаться к элементам в стеке ( lua_to...(lua_State*, index) [вместо многоточия следует поставить нужный тип, например bool string и тд] ), имеющим индексы i >= 1 i <= count. Эти элементы могут иметь разные типы, функция должна проверять перед использованием типы переданных ей аргументов (функциями lua_is...(lua_State*, index) ), так как интерпретатор lua в момент вызова функции из кода lua не выполняет таких проверок (он не проверяет даже количество переданных в функцию аргументов, не то что их тип ). Чтобы возвратить значение, функция должна поместить в стэк возвращаемые значения (функции lua_push...) и возвратить их количество. Функция может вернуть множество значений (это называется кортеж), причем для функции lua абсолютно естественно, возвращать переменное число параметров. Следующий пример иллюстрирует сказанное, программа регистрирует функцию print_arg, которая печатает количество переданных ей параметров, сами параметры и возвращает количество параметров. Скрипт использует эту функцию по назначению.
#include "stdafx.h"
extern "C" {
#    include "lua.h"
#    include "lauxlib.h"
#    include "lualib.h"
}
#include <stdlib.h>
#include <stdio.h>
#include <string>
//Эта функция может принимать переменное число параметров через L
int stack_print (lua_State* L)
{
    int n = lua_gettop(L);//получаем количество параметров переданных функции
    printf("args: %d\n", lua_gettop(L) );
    for (int i = 1; i <= n; ++i)
    {
        if (lua_isnumber(L, i))//проверяем тип i-го параметра
            //если это вещественное число выводим его на экран
            printf("result[%d] = %f\n", i, lua_tonumber(L, i));
        else if (lua_isstring(L, i))//проверка на строку
            printf("result[%d] = %s\n", i, lua_tostring(L, i));
        else if (lua_istable(L, i))//если таблица
        {    lua_pushnil(L);//кладем на вершину стека NULL
            while ( lua_next(L, i) )//эта функция берет значение из стека (по индексу -1),
            //и использует его в качестве ключа для поиска по таблице, которая
            //находится в стеке по индексу i, в случае если находит, добавляет в
            //стек пару ключ - значение, следующие в таблице после ключа находившегося в стеке
            //если больше нет значений, ф-я возвращает 0
            //lua_next используется для перебора элементов таблицы
            {
                /*
                    Состояние стека:
                    [-1] value  <--Вершина стека
                    [-2] key
                    ....
                    [i] table <-- текущий элемент
                 */
                //print key value
                if (lua_type(L, -2) == LUA_TNUMBER)
                {
                    printf ( "key = %f", lua_tonumber(L, -2) );
                } else if ( lua_type(L, -2) )
                {
                    printf ("key = %s", lua_tostring(L, -2));
                }
                //print value
                if (lua_type(L, -1) == LUA_TNUMBER)
                {
                    printf ( "\tvalue = %f\n", lua_tonumber(L, -1));
                } else if ( lua_type(L, -1) )
                {
                    printf ("\tvalue = %s\n", lua_tostring(L, -1));
                }
                //удаляем значение value из стэка, что-бы на следующей итерации
                //ключ использовался для нахождения следующей пары значений из таблицы
                lua_pop(L, 1);
                /*
                    Состояние стека:
                    [-1] key  <--Вершина стека
                    ....
                    [i] table <-- текущий элемент
                 */
            }
        }
        else if (lua_isuserdata(L, i)) //пользовательский тип данных
        {
            void* udata = lua_touserdata(L, i);
            printf("lua userdata %06", udata);
        }
    }
    //возвпащаем один параметр - количество переданных значений
    lua_pushinteger(L, (ptrdiff_t)n);
    return 1;//сие означает что возвращается одно значение
}
int main(void)
{
    int status, result;
    lua_State *L;
    L = lua_open();//Новая виртуальная машина луа
    
    luaL_openlibs(L);//Открываем библиотеки base table string io math 
    lua_register(L,"print_arg", &stack_print);//Регистрируем функцию
    
    
    status = luaL_loadfile(L, "script.lua");//загружаем файл
    if (status) {
        printf("file not found or syntax error\n");
        return 1;
    }
    result = lua_pcall(L, 0, LUA_MULTRET, 0);//выполняем программу, 
    //точка входа в которую, расположена на вершине стека
    if (result) {
        printf("runtime error\n");
        return 1;
    }
    lua_close(L); //Done
    return 0;
}
код скрипта:
-- script.lua
x = 8
x = print_arg(5, x, 'lua')
io.write('x = '..x..'\n')
local table        = {}
table['pi']    = 3.14159
table[3.14159]    = 'pi'
print_arg(table)
Этот пример, уже намного более практичен. С помощю склеивающих функций вы можете изменять часть логики, не перекомпилируя проект, просто изменив скрипт. В следующей статье я покажу как реализовать в lua возможность, которая не поддерживается этим языком напрямую – объектно ориентированное программирование.

пятница, 18 января 2008 г.

Обработка ошибок

Для себя я уяснил 2 простых правила обработки ошибок в программе. Любая необработанная ошибка должна иметь последствия. Неважно какие, главное, что-бы была возможность узнать, что ошибка произошла. Как следствие - второе правило: Должна существовать возможность узнать причину возникновения ошибки. Неважно как, но должна быть возможность установить точное место возникновения ошибки. Варианты - от сообщения в лог, с именем функции и номером строки, до создания дампа, содержащего состояние стека, и регистров в момент обнаружения ошибки. Ну а как-же быть с ошибками, не приводящим к завершению приложения? Для этого есть первое правило %) Разумеется следует пользоваться исключениями и писать безопасный с точки зрения исключений код. Возвращаемый какой нибудь функцией HRESULT, клиентский код может и не проверить, исключение он конечно то-же может и не перехватить, но по крайней мере будет ясно где ошибка. Так-же не стоит увлекаться разработкой разветвленных иерархий классов исключений, вполне хватит и одного (благодаря второму правилу и бритве Оккама :-D)

четверг, 17 января 2008 г.

Lua и C++

Я постараюсь рассказать о том как использовать интерпретатор lua в программе написанной на C++. Это может понадобиться в тех случаях, когда в программу нужно встроить поддержку скриптов, либо написать библиотеку для интерпретатора Lua. Луа – это язык предназначенный для написания расширений. Основа Луа – это библиотека написанная на ANSI С, поэтому она собирается на любой платформе. Библиотеку можно собрать в разных конфигурациях, например исключить парсер, auxiliary library или что нибудь еще. Парсер луа не использует универсальные инструменты для генерации кода, вроде yacc или lex, а написан разработчиками с нуля. Так как библиотека работает очень быстро, и использует очень мало памяти для этого (да и сама имеет очень скромные размеры), Луа может работать на самом разнообразном железе. Приступим. В первую очередь нужно скачать исходники lua с оф сайта www.lua.org. Затем вам не составит труда их собрать. Для того чтобы использовать Lua API, нужно подключить несколько заголовочных файлов:
extern "C" {
# include "lua.h"
# include "lauxlib.h"
# include "lualib.h"
}
lua.h – Lua API lauxlib.h – дополнительный API lualib.h – стандартные библиотеки. Луа использует следующее соглашение об именах функций: lua_### - функция(или макрос, или определение типа) из базового интерфейса, luaL_### - функция из auxiliary библиотеки. Чтобы иметь возможность использовать API, нужно, впервую очередь, создать lua_State. Эта структура содержит состояние виртуальной машины луа, помимо, это стек, через который lua общается с вашей программой.
lua_State *L;
    L = lua_open();//создать стек
    lua_close(L); //закрыть стек
Весь обмен информацией между программой и скриптом происходит через стэк. Например если ваша программа попросит lua создать какой нибудь объект, то он в результате будет находится на вершине стека. Если программа должна что-нибудь передать скрипту, то это что-то то-же должно быть передано через стэк (примерно так: lua_pushnumber(L, 0) ). Ваша программа может создать несколько экземпляров lua_State. К элементам стека можно обращаться по индексу, положительные значения соответствуют абсолютным индексам – элемент помещенный в стэк первым имеет индекс равный 1, следующий 2 и тд, абсолютные индексы элементов стека не изменяются при добавлении новых элементов в стэк. Отрицательные значения используются для индексирования относительно вершины стека, верхний элемент имеет индекс -1, следующий за ним -2 и тд. При добавлении нового элемента в стэк относительные(отрицательные) индексы всех его элементов изменяются. В общем стек луа, из 3х элементов, выглядит примерно так: [abs][rel] [-1][3] <--Top [-2][2] [-3][1]<—Bottom Ок. В первом примере показывается как создать экземпляр виртуальной машины lua, загрузить скрипт, передать в него значения (через глобальную переменную), выполнить скрипт и получить результат его работы.
#include "stdafx.h"

extern "C" {
#    include "lua.h"
#    include "lauxlib.h"
#    include "lualib.h"
}
#include <stdlib.h>
#include <stdio.h>



int main(void)
{
    int status, result, i;
    double sum;
    lua_State *L;

    L = lua_open();//Создаем стэк
    
    luaL_openlibs(L);//подключить стандартные библиотеки   
    
    status = luaL_loadfile(L, "script.lua");//загрузить файл
    //содержимое файла обрабатывается, создается функция, которая помещается на вершину стэка
    /* Состояние стэка:
        [-1]main <--top of the stack
     */
    if (status)
    {
        (void)fprintf(stderr, "file not found\n");
        exit(1);
    }

    lua_newtable(L);//создать таблицу, поместить ее на вершину стэка
        /* Состояние стэка:
            [-1]table <--top of the stack
            [-2]main
         */

    //В этом цикле таблица заполняется значениями
    for (i = 1; i <= 5; i++) 
    {
        /* Состояние стэка:
            [-1]table <--top of the stack
            [-2]main
         */
        lua_pushnumber(L, i);//кладем в стэк число (key)   
        /* Состояние стэка:
            [-1]key <--top of the stack
            [-2]table
            [-3]main
         */
        lua_pushnumber(L, i*2);//добавляем значение ключа (value)
        /* Состояние стэка:
            [-1]value <--top of the stack
            [-2]key
            [-3]table
            [-4]main
         */
        lua_rawset(L, -3);//добавить к таблице пару ключ-значение: table[key] = value
        //таблица (table) должна распологаться по индексу -3, value - на вершине стэка
        //key - после value
        /* Состояние стэка:
            [-1]table <--top of the stack
            [-2]main
         */
    }
    
    lua_setglobal(L, "foo");//добавить глобальную переменную "foo" в скрипт
    //значение переменной (в данном случае это будет таблица)
    //должно находится на вершине стэка
    /* Состояние стэка:
        [-1]main <--top of the stack
     */    

    result = lua_pcall(L, 0, LUA_MULTRET, 0);//Выполняет функцию, функция должна находится
    //на вершине стэка (функция находящаяся на вершине стэка была создана через luaL_loadfile).
    //В данном случае ф-я не получает аргументов.

    if (result) {
        fprintf(stdout, "runtime error\n");
        exit(1);
    }

    sum = lua_tonumber(L, lua_gettop(L));//Получаем через стэк значение которое вернул скрипт
    if (!sum) {
        fprintf(stdout, "lua_tonumber() failed!\n");
        exit(1);
    }

    fprintf(stdout, "script exec result: %.0f\n", sum);

    lua_pop(L, 1);//удаляем число которое вернул скрипт из стэка 
    lua_close(L);//удаляем стэк и интерпретатор 

    return 0;
}
Файл script.lua должен выглядеть так:
-- script.lua
x = 0
for i = 1, #foo do
print(i, foo[i])
x = x + foo[i]
end
return x
Скрипт суммирует значения всех элементов таблицы в переменной х, и возвращает результат.

воскресенье, 13 января 2008 г.

Hello World

Интересно, что я буду сюда писать...