Как преобразовать указатель на символ в указатель на пользовательский объект - PullRequest
2 голосов
/ 24 января 2012

Я использую leveldb для хранения пар ключ-значение целочисленных и MyClass объектов. На самом деле ключ может содержать более одного из этих объектов. У меня возникает проблема при получении данных из базы данных. Он компилируется, однако значения членов MyClass не являются теми, которые я положил в базу данных.

std::string value;
leveldb::Slice keySlice = ANYKEY;
levelDBObj->Get(leveldb::ReadOptions(), keySlice, &value);

std::string value1 теперь может содержать только один объект MyClass или более. Так как мне их получить?

Я уже попробовал следующее, которое не сработало;

1.) Непосредственное приведение типов и memcpy

std::vector<MyClass> vObjects;
MyClass* obj = (MyClass*)malloc( value.size());
memcpy((void*)obj, (void*) (value.c_str()), value.size());
MyClass dummyObj;
int numValues = value.size()/sizeof(MyClass);
for( int i=0; i<numValues; ++i) {
    dummyObj = *(obj+i);
    vObjects.push_back(dummyObj);
}

2.) Reinterpret_cast для аннулирования указателя

MyClass* obj = (MyClass*)malloc( value.size());
const void* vobj = reinterpret_cast<const void*>( value.c_str() );
int numValues = value.size()/sizeof(MyClass);
for( int i=0; i<numValues; ++i) {
    const MyClass dummyObj = *(reinterpret_cast<const MyClass*>(vobj)+i);
    vObjects.push_back(dummyObj);
}

MyClass - это коллекция из нескольких открытых участников, например, unsigned int и unsigned char, и он имеет стабильный размер.

Я знаю, что есть похожие проблемы только с одним объектом. Но в моем случае вектор может содержать более одного, и это происходит из базы данных leveldb.

РЕДАКТИРОВАТЬ: РЕШЕНИЕ

Я написал (де) метод сериализации для MyClass, который затем заставил его работать. Спасибо за подсказку!

void MyClass::serialize( char* outBuff ) {
    memcpy(outBuff, (const void*) &aVar, sizeof(aVar));
    unsigned int c = sizeof(aVar);
    memcpy(outBuff+c, (const void*) &bVar, sizeof(bVar));
    c += sizeof(bVAr);
    /* and so on */
}

void MyClass::deserialize( const char* inBuff ) {
    memcpy((void*) &aVar, inBuff, sizeof(aVar));
    unsigned int c = sizeof(aVar);
    memcpy((void*) &aVar, inBuff+c, sizeof(aVar));
    c += sizeof(aVar);
    /* and so on */
}

Метод get выглядит следующим образом (put аналогично):

int getValues(leveldb::Slice keySlice, std::vector<MyObj>& values) const {
    std::string value;  
    leveldb::Status status = levelDBObj->Get(leveldb::ReadOptions(), keySlice, &value);
    if (!status.ok()) {
        values.clear();
            return -1;
    }

    int nValues = value1.size()/sizeof(CHit);
    MyObj dummyObj;
    for( int i=0; i<nValues; ++i) {
        dummyObj.deserialize(value.c_str()+i*sizeof(MyObj));
        values.push_back(dummyObj);
    }

    return 0;
}               

1 Ответ

2 голосов
/ 24 января 2012

Вы должны сериализовать свой класс ... в противном случае вы просто берете немного памяти и записываете ее в leveldb. Все, что вы получите, не только будет другим, но и, вероятно, совершенно бесполезным. Проверьте этот вопрос для получения дополнительной информации о сериализации: Как вы сериализуете объект в C ++?

LevelDB поддерживает поддержку нескольких объектов под одним ключом, однако стараются избегать этого, если у вас нет действительно веской причины. Я бы порекомендовал хешировать каждый объект с уникальным хэш (см. Google CityHash , если вы хотите функцию хеширования) и сохраните сериализованные объекты с их соответствующим хешем. Если ваши объекты сами по себе являются коллекцией, то вы должны сериализовать все ваши объекты в массив байтов и иметь некоторый метод, позволяющий определить, где начинается / заканчивается каждый объект.

Обновление

Сериализуемый класс будет выглядеть примерно так:

class MyClass
{
private:
    int _numeric;
    string _text;
public:
    // constructors 

    // mutators
    void SetNumeric(int num);
    void SetText(string text);

    static unsigned int SerializableSize()
    {
        // returns the serializable size of the class with the schema:
        // 4 bytes for the numeric (integer) 
        // 4 bytes for the unsigned int (the size of the text)
        // n bytes for the text (it has a variable size)
        return sizeof(int) + sizeof(unsigned int) + _text.size();
    }

    // serialization
    int Serialize(const char* buffer, const unsigned int bufferLen, const unsigned int position)
    {
        // check if the object can be serialized in the available buffer space
        if(position+SerializableSize()>bufferLen)
        {
            // don't write anything and return -1 signaling that there was an error
            return -1;
        }

        unsigned int finalPosition = position;

        // write the numeric value
        *(int*)(buffer + finalPosition) = _numeric;

        // move the final position past the numeric value
        finalPosition += sizeof(int);

        // write the size of the text
        *(unsigned int*)(buffer + finalPosition) = (unsigned int)_text.size();

        // move the final position past the size of the string
        finalPosition += sizeof(unsigned int);

        // write the string
        memcpy((void*)(buffer+finalPosition), _text.c_str(), (unsigned int)_text.size());

        // move the final position past the end of the string
        finalPosition += (unsigned int)_text.size();

        // return the number of bytes written to the buffer
        return finalPosition-position;
    }

    // deserialization
    static int Deserialize(MyClass& myObject, 
        const char* buffer, 
        const unsigned int buffSize, 
        const unsigned int position)
    {
        insigned int currPosition = position;

        // copy the numeric value
        int numeric = *(int*)(buffer + currentPosition);

        // increment the current position past the numeric value
        currentPosition += sizeof(int);

        // copy the size of the text
        unsigned int textSize = *(unsigned int*)(buffer + currentPosition);

        // increment the current position past the size of the text
        currentPosition += sizeof(unsigned int);

        // copy the text
        string text((buffer+currentPosition), textSize);

        if(currentPosition > buffSize)
        {
            // you decide what to do here
        }

        // Set your object's values
        myObject.SetNumeric(numeric);
        myObject.SetText(text);

        // return the number of bytes deserialized
        return currentPosition - position;
    }
};
...