Динамический рекурсивный контейнер
От: Djafar Россия  
Дата: 26.11.09 15:03
Оценка:
Приветствую всех. Заранее прошу прощения за большой объем текста — я не нашел спойлеров, что бы код закатать под них.

Суть проблемы такова — у меня опять стойкое ощущение, что я изобретаю велосипед, но тем не менее ничего удовлетворяющего потребностям я не нашел (вполне вероятно, плохо искал). Значит к делу:

Задача такова — модуль D (некий демон) принимает от клиента xml файл c 1..n аргументов. Так же в этом файле указан модуль X и функция этого модуля , которая должна обработать запрос. Модуль D обрабатывает xml и обертывает аргументы в некий контейнер, который передает в вызываемую функцию.
Ремарка_1: модуль D знать не знает до поступления запроса ни о модуле X, ни о принимаемых/возращаемых аргументах функции.
Далее, из функции (dlopen,dlsym) модуль D получает аналогичный контейнер, с 1..n значениями, причем в контейнер может быть вложен аналогичный контейнер n раз (например, возращается таблица, набор данных или и таблица и набор данных, всякое бывает).
Ремарка_2: Так как возращаемая структура создается динамически, то логично использовать массив ссылок на собственно значения, а так как создается структура в функции, то по выходе из нее и разворачивании стека без динамического выделения памяти по указателям останеться пфу, в смысле ничего.

В принципе искомый контейнер по своему поведению напоминает ассоциативный массив в PHP. Ах, да, чуть не забыл. Аргументы естественно не типизированы, в смысле предполагается как набор интегральных значений разного типа, так и строки.

Собственно вопрос 1 — если я изобретаю велосипед — то подскажите мне варианты таких динамических рекурсивных нетипизированых контейнеров.
Вопрос 2 — в листинге ниже (жалко нет спойлеров) приведены класса, в которых я попытался реализовать данный контейнер. Но там есть две ошибки:


Собственно код:

MiracleBox.h

typedef enum {eNone=0, eInt, eString, eBool, eDouble, eDate, eArray} VariableTypes;

class Variant {
protected:
    //Для скалярных величин - указатель на хранящееся в Value значение
    //Для массивов - указатель на массив
    void* pVal;
    //Тип значения
    VariableTypes Type;
    //Для скалярных величин - размер значения
    //Для массивов - кол-во элементов
    uint Size;
    //Имя переменной/ключ ассоциативного массива
    char* Name;


public:
    Variant(): pVal(0),Type(eNone),Size(0),Name(0){};//конструктор по умолчанию, создающий ссылку на пустое значение

    Variant(int val);
    Variant(const char* val);
    Variant(string val);
    Variant(bool val);
    Variant(double val);
    Variant(time_t val);
    Variant(const Variant& val);
    virtual ~Variant(){};

    Variant& operator=(const Variant& val);


    virtual void Free();
    //Обнуление переменной без очищения памяти
    virtual void SetNull(){pVal=0;Size=0;Type=eNone;}
    void SetName(const char* val);
    char* GetName() const {return Name;}
    VariableTypes GetType() const {return Type;}
    //void* GetValuePtr() const {return pVal;}

    string ToString() const {return ToCString();}
    char* ToCString() const;
    int ToInt() const {return ToDouble();}
    double ToDouble() const;
    bool ToBool() const {return ToDouble();}
    time_t ToTimestamp() const {return ToDouble();}

};
///////////////////////////////////////////////
//класс Array

class Array: public Variant {


public:
    Array();
    //не реализован, так как va-макросы не работают с non-POD типами, n объектов передать нельзя.
    //Array(Variant* ...);
    //

    //explicit Array(uint count);
    Array(const Array& val);
    virtual ~Array(){};

    Variant& operator[](uint index);
    Variant& operator[](const char* key);
    Array& operator=(const Array& val);

    void Push (Variant& val,const char* key=0);
    virtual void Free();


    bool IsEmpty(){    return (Size==0);}
    int GetArraySize() const { return Size;}



};

MiracleBox.cpp

/////////////////////////////////////////////////////////////////////
//класс Variant
//конструкторы
Variant::Variant(int val){//конструктор для целых чисел
    pVal = new int(val);
    Type = eInt;
    Size = sizeof(int);
}
Variant::Variant(const char* val){
    int len = strlen(val)+1;
    char* buf = new char[len];
    strcpy(buf,val);
    pVal = buf;
    Type = eString;
    Size = len-1;
}
Variant::Variant(string val){
    int len = val.size()+1;
    char* buf = new char[len];
    strcpy(buf,val.c_str());
    pVal = buf;
    Type = eString;
    Size = len-1;
}
Variant::Variant(double val){
    pVal = new double(val);
    Type = eDouble;
    Size = sizeof(double);
}
Variant::Variant(bool val){
    pVal = new bool(val);
    Type = eBool;
    Size = sizeof(bool);
}
Variant::Variant(time_t val){
    pVal = new time_t(val);
    Type = eDate;
    Size = sizeof(time_t);
}
Variant::Variant(const Variant& val){
    pVal = val.pVal;
    Type = val.Type;
    Size = val.Size;
}



Variant& Variant::operator=(const Variant& val){
    if(this!=&val){
        this->Free();
        pVal = val.pVal;
        Type = val.Type;
        Size = val.Size;
    }
    return *this;
}

void Variant::SetName(const char* val){
    if(val && val[0]){//если строка существует и не равна пустой строке
        delete Name;
        int len = strlen(val)+1;
        Name = new char[len];
        strcpy(Name,val);
    }
}


void Variant::Free(){
    //Так как метод вызывается из копирующего присваивания, а значит и при присваивании
    //индексированному элементу массива(вновь созданный элемент массива до присваивания имеет тип
    //eNone), то для Variant == NULL очистка не происходит.
        if(Type){
        switch(Type){
        case (eInt):
            delete (int*)pVal;
            break;
        case (eBool):
            delete (bool*)pVal;
            break;
        case (eString):
            delete (char*)pVal;
            break;
        case (eDate):
            delete (time_t*)pVal;
            break;
        case (eDouble):
            delete (double*)pVal;
            break;
        case (eArray)://так как функция фиртуальная - сюда никогда не должны попадать, но попадаем, так как в operator[] выделяем память под Variant
            ((Array*)pVal)->Free();
            delete (Variant*)pVal;
        default:
            throw new MiracleBoxException("Ошибка освобождения памяти в Variant");

           }
        }
}

double Variant::ToDouble() const{
    switch(Type){
        case(eBool):
        case (eInt):
        case (eDate):
        case (eDouble):
            return *(double*)pVal;
        case (eString):
        case (eArray):
            return Size;
        case eNone:
        default:
            return 0;
    }
}
char* Variant::ToCString() const{
    char* s;
    switch(Type){
        case(eBool):
        case (eInt):
        case (eDate):
            sprintf(s,"%d",*(int*)pVal);
            return s;
        case (eDouble):
            sprintf(s,"%f",*(double*)pVal);
            return s;
        case (eString):
            return (char*)pVal;
        case (eArray):
            return s;
        case eNone:
        default:
            return s;
    }
}

/////////////////////////////////////////////////////
//класс Array
Array::Array(){
    pVal = 0;//new Variant*[0];
    Size = 0;
    Type = eArray;
}

Array::Array(const Array& val){
    pVal = val.pVal;
    Type = val.Type;
    Size = val.Size;
}

Array& Array::operator =(const Array& val){/////////
    if(this!=&val){
            this->Free();
            pVal = val.pVal;
            Type = val.Type;
            Size = val.Size;
    }
    return *this;
}

Variant& Array::operator[](uint index){
    Variant** p = (Variant**)pVal;
    if(index < Size && index >= 0)
        return *p[index];
    else
        throw MiracleBoxException("Range exception");
}
Variant& Array::operator[](const char* key){

    Variant** p = (Variant**)pVal;

    for(uint i = 0; i < Size; ++i){

        if(p[i]->GetName()){

            if(!strcmp(key,p[i]->GetName())){
                return *p[i];
            }
        }
    }
    //ключ не найден, создаем новый элемент
    Variant v;
    Push(v,key);
    return *((Variant**)pVal)[Size-1];
}
void Array::Push(Variant& val, const char* key){
    Variant* pNew = new Variant();
    *pNew = val;
    Variant** p = (Variant**)pVal;
    uint newArrSize=Size+1;
    Variant** temp = new Variant*[newArrSize];
    for(uint i = 0; i < Size; ++i){
        temp[i] = p[i];
    }
    delete[] p;
    temp[Size] = pNew;
    temp[Size]->SetName(key);

    pVal = temp;
    Size = newArrSize;

}

void Array::Free(){
    Variant** p = (Variant**)pVal;
    for(uint i = 0;i < Size; i++){
        try{
            delete p[i]->GetName();
            if(p[i]->GetType()==eArray){
                Array* a = static_cast<Array*>(p[i]);
                a->Free();
            }else{
                p[i]->Free();
            }
            delete p[i];
        }
        catch(...){
            //int i = errno;
            //char* p = strerror(errno);
        }
    }
    delete[] p;
}


Пример функции, в которой принимаются значения от модуля D и формируется возвращаемый объект

#define ARGS (*args)
#define RETVAL (*retval)

extern "C" int RegisterUser(Array* args, Array* retval){

    Array test;
    test["age"]=35;
    test["mark"]=4.73;
    Array row1,row2;
    row1["id"] = 1;
    row1["name"] = "Vasya";
    row1["date"] = time(NULL);
    row2["id"] = 2;
    row2["name"] = "Petya";
    row2["date"] = time(NULL);
    Array table;
    table.Push(row1);
    table.Push(row2);

    int result=0;
    string sessionID;


    UserModule um;
        result = um.RegisterUser(ARGS["login"].ToString(), ARGS["password"].ToString() ,sessionID);
        RETVAL["sessionID"] = sessionID;
        RETVAL["success"] = (result==0);
        RETVAL["test_array"] = test;
        RETVAL["test_table"] = table;
return 0;
}
Не судите строго и не кидайтесь тапками, после многих лет работы с виндоус и нескольких лет работы с .NET пришлось перейти к linux и вернуться к забытым с/с++.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.