Как решить проблему с шаблоном, чтобы сохранить разные типы данных в файл? - PullRequest
2 голосов
/ 17 июля 2010

Я столкнулся с новой проблемой при использовании шаблонов.Это я немного творческий из книги, которую я недавно прочитал, расширяя его идеи, и в ней есть этот пример кода.

Скажем, у вас есть шаблонный класс Array2D.И у вас есть этот метод (Array2D :: WriteFile):

    bool WriteFile( const char* p_filename )
{
    FILE* outfile = 0;
    int written = 0;

    // open the file
    outfile = fopen( p_filename, "wb" );

    // return if it couldn't be opened
    if( outfile == 0 )
        return false;

    // write the array and close thef ile
    written = fwrite( m_array, sizeof( Datatype ), m_size, outfile );
    fclose( outfile );

    // if we didn't write the number of items we expected,
    // return failure
    if( written != m_size )
        return false;

    // return success.
    return true;
}

Это прекрасно работает для базовых типов данных, таких как int, char, float и т. Д. Но что, если вам понадобится немного творческого подхода к созданию типа данныхсоставная переменная, такая как SLinkedList (односвязный список).Это был бы Array2D.Затем вы вызываете свою старую функцию WriteFile ().Это сохранит его, но вы только что сохранили только один узел.Остальные узлы ушли в забвение, чтобы никогда не возвращаться.

Мне пришло в голову несколько идей: 1. Бросай пальцы, наблюдая, как часы идут к твоему сроку.2. Сделайте Array2D :: WriteFile () чистой виртуальной функцией, заставляя более специфический класс сохранять его так, как он должен.Но тогда я не могу использовать Array2D сам по себе в других автономных ситуациях.3. Напишите указатель на функцию для сохранения, но я думаю, что это может привести к путанице, потому что вы не знаете, какие данные вы передадите, пока не придет время.Это может варьироваться в зависимости от типа данных.4. Поймите, шаблоны не являются решением, возможно.

Шаблонный класс не решает каждый сценарий для меня на основе типа данных.Так что, по вашему мнению, было бы хорошим решением?Спасибо!

Ответы [ 3 ]

4 голосов
/ 17 июля 2010

Ваши объекты должны быть сериализуемыми .Подумайте о том, что на самом деле происходит под капотом, когда вы пытаетесь таким образом записать заголовок связанного списка - конечно, он просто пишет первый узел;функция write понятия не имеет о чем пишет , поэтому она не имеет понятия, как следовать указателям на следующий узел и записывать это тоже.Он просто видит биты и записывает их до конца текущего объекта, который в данном случае является просто первым узлом.

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

Теперь ваш вопрос помечен как C ++, но код, который вы написали, очень C-лайк.Обычный способ реализовать сериализуемый объект в C ++ - переопределить оператор << с аргументом std::ostream& и возвращаемым значением, например

 std::ostream& MyType::operator<< (std::ostream& out)
 {
    // Here, write out the logical contents of this object in whatever format you
    // feel is appropriate (keep in mind endianness and floating point representation
    // if you want portability!)
    return out << this->field1 << this->field2 << this->field3;
 }

И симметрично

 std::istream& MyType::operator>> (std::istream& in)
 {
    // Here, read in the logical contents of this object in the same format
    return in >> this->field1 >> this->field2 >> this->field3;
 }

Теперь, вы можете просто сделать что-то вроде этого:

 MyType t;
 std::ofstream outputFile("myoutputfile.dat");

 outputFile << t;

Обратите внимание, что этот подход переопределения операторов потока означает, что, если, например, MyType::field2 окажется сам по себе сложным объектом, он все равно будет сериализован идесериализовано должным образом в вышеприведенном коде до тех пор, пока его потоковые операторы будут переопределены.

Но если вы хотите продолжить использовать файловый ввод-вывод в стиле C, как в исходном посте, ничего не происходитне будет так чисто.Если ваш код действительно должен быть C ++, вы должны использовать библиотеки iostream для ввода / вывода файлов.

2 голосов
/ 17 июля 2010

Вы можете просто использовать iostreams эквивалент.Вместо использования fwrite, которая является функцией C и работает только с типами данных C, используйте эквивалент C ++, который может быть перегружен и специализирован для пользовательских типов.типы, а для пользовательских типов вы можете определить его самостоятельно.

1 голос
/ 17 июля 2010

Моя немедленная реакция заключается в том, что вы смотрите на проблему, которая рассматривалась (и решалась, по крайней мере до некоторой степени) раньше. Да, он все еще может использовать шаблоны довольно интенсивно. Я бы взглянул на std::copy вместе с std::ostream_iterator, а также на некоторые библиотеки сериализации, такие как Boost serialization .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...