Увеличьте сериализацию «нарушение прав чтения» при выводе в файл через ofstream - PullRequest
1 голос
/ 10 апреля 2020

Я заранее прошу прощения за небольшой кусочек кода.

У меня есть довольно простой класс Player в игре, которую я создаю. После того, как пользователь вводит имя пользователя, я go в директории сохранения, которая должна содержать все файлы сохранения плеера. Если файл не может быть найден, предполагается, что программа создаст новый объект игрока для использования в оставшейся части игры.

Сразу после создания этого нового объекта я хочу создать новый файл сохранения для этого нового проигрывателя. Создание файла с std::ofstream работает просто отлично. Проблема возникает, когда я пытаюсь сериализовать класс проигрывателя в этот файл. В частности, моя система выдает «нарушение прав чтения» при использовании перегруженного оператора << из boost::archive::text_oarchive.

Вот соответствующие части моего кода:

// Inside the main function
Player player;
const auto player_name = login_menu.get_value();
const auto sav_dir = fs::current_path() / "sav";
bool player_exists = Player::exists(player_name, sav_dir);
if (player_exists)
{
    cons::File player_file(sav_dir / (player_name + ".dat"),
        cons::fopenmode::input);
    if (player_file.get_ifstream().is_open())
    {
        boost::archive::text_iarchive
            player_file_archive(player_file.get_ifstream());

        player_file_archive >> player;
    }
    else
    {
        cons::print(
            "",
            "ERROR: Save file was located, but cannot be loaded.",
            "It may be corrupted. Deleting save file."
        );
        cons::pause();
        player_file.delete_file();
        player_exists = false;
    }
}
if (!player_exists) // No player found; make new player
{
    player = Player(player_name, 1, 0, 1.0f);
    std::ofstream player_file(sav_dir / (player_name + ".dat"));

    if (!player_file.is_open()) // TODO Remove me
        cons::print("ERROR: OFSTREAM NOT OPENED");

    { // Create new save file
        boost::archive::text_oarchive 
            player_file_archive(player_file);

        player_file_archive << player; // TODO FIXME Throwing read access violation
    }
}

Для дополнительных помогите, вот класс Player (без реализации):

class Player
{
public:
    Player();
    Player(std::string name, unsigned short level, int money, float stealth);

    // ... Getter and setters ...

private:
    friend class boost::serialization::access;

    std::string name_;
    unsigned short level_;
    int money_;
    float stealth_;

    template <class Archive>
    /**
     Used for Boost library data serialization

     @param ar      Archive reference
     @param version Unused variable needed for Boost compatibility
    */
    void serialize(Archive& ar, const unsigned version)
    {
        ar & name_;
        ar & level_;
        ar & money_;
        ar & stealth_;
    }

public:
    /**
     Searches for a player's save file in a directory

     @param player_name The name of the player
     @param save_dir    The directory to search for the player's save file
     @returns 'true' if the player's save file is found; 'false' otherwise
    */
    static bool exists(const std::string& player_name, 
        const std::filesystem::path& save_dir);
};

Если кто-нибудь знает, как исправить мою проблему, это будет очень цениться. Вот некоторый консольный журнал исключения:

'Ditacker.exe' (Win32): Loaded 'C:\Dev\C++\Games\Console Games\Ditacker\Ditacker\bin\Debug-x64\Ditacker.exe'. Symbols loaded.
'Ditacker.exe' (Win32): Loaded 'C:\Windows\System32\ntdll.dll'. Symbols loaded.
'Ditacker.exe' (Win32): Loaded 'C:\Windows\System32\kernel32.dll'. Symbols loaded.
'Ditacker.exe' (Win32): Loaded 'C:\Program Files\AVG\Antivirus\aswhook.dll'. 
'Ditacker.exe' (Win32): Loaded 'C:\Windows\System32\KernelBase.dll'. Symbols loaded.
'Ditacker.exe' (Win32): Loaded 'C:\Dev\C++\Games\Console Games\Ditacker\Ditacker\bin\Debug-x64\boost_serialization-vc142-mt-gd-x64-1_72.dll'. Symbols loaded.
'Ditacker.exe' (Win32): Loaded 'C:\Windows\System32\msvcp140d.dll'. Symbols loaded.
'Ditacker.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140d.dll'. Symbols loaded.
'Ditacker.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140_1d.dll'. Symbols loaded.
'Ditacker.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbased.dll'. Symbols loaded.
The thread 0x1e910 has exited with code 0 (0x0).
Exception thrown: read access violation.
**_Pg** was 0xFFFFFFFFFFFFFFFF.

The program '[121404] Ditacker.exe' has exited with code 0 (0x0).

Заранее спасибо!

PS - Пространство имен cons содержит различные функции ввода-вывода консоли. Используемые здесь должны быть самоочевидными, но я могу предоставить больше информации о них, если это необходимо.

1 Ответ

0 голосов
/ 22 апреля 2020

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

Если у вас все еще есть «cra sh», тогда он должен происходят из другого места. Вероятно, объект проигрывателя устарел (вы действительно передаете его по ссылке или по указателю в своем коде?).

Вы можете использовать Asan / UBSan для поиска подобных ошибок. Кроме того, valgrind.

В противном случае, опубликуйте автономную программу, которая демонстрирует cra sh.

DEMO TIME

Смотрите здесь живую демонстрацию, в которой реализованы счастье и ошибка. пути:

Live On Coliru

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/serialization/string.hpp>
#include <filesystem>
#include <fstream>
#include <iostream>

namespace fs = std::filesystem;

class Player {
  public:
    Player() = default;
    Player(std::string name, unsigned short level, int money, float stealth)
            : name_(std::move(name)), level_(level), money_(money),
              stealth_(stealth) {}

    // ... Getter and setters ...

  private:
    friend class boost::serialization::access;
    friend std::ostream& operator<<(std::ostream& os, Player const& player) {
        return os << "Player {" << std::quoted(player.name_)
                  << ", level:" << player.level_ << ", money:" << player.money_
                  << ", stealth:" << player.stealth_ << " }";
    }

    std::string name_;
    unsigned short level_;
    int money_;
    float stealth_;

    template <class Archive>
    /**
     Used for Boost library data serialization

     @param ar      Archive reference
     @param version Unused variable needed for Boost compatibility
    */
    void serialize(Archive& ar, unsigned /*unused*/) {
        ar& name_& level_& money_& stealth_;
    }

  public:
    /**
     Searches for a player's save file in a directory

     @param player_name The name of the player
     @param save_dir    The directory to search for the player's save file
     @returns 'true' if the player's save file is found; 'false' otherwise
    */
    static bool exists(const std::string& player_name,
                       const std::filesystem::path& save_dir) {
        return fs::exists((save_dir / player_name).replace_extension(".dat"));
    }
};

struct {
    static std::string get_value() { return "player_name"; }
} static login_menu;

namespace cons {
    namespace fopenmode {
        static auto const input = std::ios::in;
    }
    struct File {
        std::string fname;
        std::ifstream ifs;
        File(std::string fname, std::ios::openmode mode)
                : fname(fname), ifs(fname, mode) {
            ifs.exceptions(std::ios::badbit | std::ios::failbit);
        }
        auto& get_ifstream() { return ifs; }
        bool delete_file() {
            if (ifs.is_open()) {
                ifs.close();
            }
            return fs::remove(fname);
        }
    };
    template <typename... T> void print(T const&... v) {
        for (auto&& frag : { boost::lexical_cast<std::string>(v)... }) {
            std::cout << frag << std::endl;
        }
    }
    void pause() {
        std::cout << "(press Enter to continue)\n";
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
} // namespace cons

int main() {
    // Inside the main function
    const auto player_name = login_menu.get_value();
    auto const sav_dir = fs::current_path() / "sav";
    fs::create_directories(sav_dir);
    auto const fname = (sav_dir / player_name).replace_extension(".dat");

    if (Player::exists(player_name, sav_dir)) {
        std::cout << "Opening existing file " << fname << "\n";
        cons::File player_file(fname, cons::fopenmode::input);

        try {
            boost::archive::text_iarchive player_file_archive(
                player_file.get_ifstream());

            Player player;
            player_file_archive >> player;

            std::cout << "Player: " << player << "\n";
        } catch (std::exception const& e) {
            cons::print(e.what(),
                        "ERROR: Save file was located, but cannot be loaded.",
                        "It may be corrupted. Deleting save file.");
            cons::pause();
            player_file.delete_file();
        }
    }

    if (!Player::exists(player_name, sav_dir))
    { // No player found; make new player
        std::cout << "Creating save file " << fname << "\n";
        Player player(player_name, 1, 0, 1.0f);
        std::ofstream player_file(fname);

        { // Create new save file
            boost::archive::text_oarchive player_file_archive(player_file);
            player_file_archive << player;
        }
    }
}

Выполнение

g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization -o test.exe
(./test.exe; ./test.exe; sed -i 's/serializa/corrup/' sav/*; ./test.exe; ./test.exe) < /dev/null
head sav/*

Печать

Creating save file "/tmp/1587589055-765971644/sav/player_name.dat"
Opening existing file "/tmp/1587589055-765971644/sav/player_name.dat"
Player: Player {"player_name", level:1, money:0, stealth:1 }
Opening existing file "/tmp/1587589055-765971644/sav/player_name.dat"
invalid signature
ERROR: Save file was located, but cannot be loaded.
It may be corrupted. Deleting save file.
(press Enter to continue)
Creating save file "/tmp/1587589055-765971644/sav/player_name.dat"
Opening existing file "/tmp/1587589055-765971644/sav/player_name.dat"
Player: Player {"player_name", level:1, money:0, stealth:1 }
22 serialization::archive 17 0 0 11 player_name 1 0 1.000000000e+00
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...