TTF_RenderText_ Solid (...) вызывает ошибку ошибки сегментации - PullRequest
0 голосов
/ 28 мая 2020

Я пытаюсь использовать класс Text для обработки текста в моей программе на основе c ++ SDL2.

TTF_RenderText_ Solid отлично работает в основной функции, хотя в моем классе Text в этой строке здесь SDL_Surface *surface = TTF_RenderText_Solid( font, text.c_str(), fg );, это вызывает некоторые ошибки. Иногда это вызывает ошибку сегментации, иногда нет.

Я отладил код, и все три переменные, TTF_Font *font, std::string text и SDL_Color fg, были с соответствующими правильными значениями.

Моя основная функция:

#include <iostream>

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

#include "Text.h"

int main()
{
    try
    {
        SDL_Init( SDL_INIT_VIDEO );
        TTF_Init();

        SDL_Window *window = SDL_CreateWindow( "Window", SDL_WINDOWPOS_CENTERED, 
        SDL_WINDOWPOS_CENTERED, 800, 800, SDL_WINDOW_SHOWN );
        SDL_Renderer *renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED );


        TTF_Font *f = TTF_OpenFont( "cruft.ttf", 32 );
        SDL_Surface *s = TTF_RenderText_Solid( f, "Avocado", {0,0,0,255} );
        if(s == NULL)
            std::cout << "s == NULL\n";



        Text title;
        title = Text( renderer, "cruft.ttf", 32, "title" );
        title.render_Text_Solid( "Avocado", {0,0,0,255} );



        SDL_Quit();
        TTF_Quit();

        return 0;
    }
    catch( std::exception& e )
    {
        std::cerr << "Error: " << e.what() << "\n";
        return 0;
    }
    catch(...)
    {
        std::cerr << "Unkown error!\n";
        return 0;
    }
}

Мой Text.cpp файл:

#include <iostream>
#include <string.h>

#include "Text.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

//Constructors
Text::Text(){}

Text::Text(SDL_Renderer *renderer, std::string file, int ptsize, std::string name)
{
    set_renderer( renderer );
    this->name = name;

    set_TTF_Font( file, ptsize );
}

Text::~Text()
{
    TTF_CloseFont( font );
    SDL_DestroyTexture( texture );
}



void Text::set_renderer( SDL_Renderer *renderer )
{
    if( renderer == NULL )
        throw std::runtime_error( name + ": Renderer could not be set! renderer == NULL\n" + SDL_GetError() );

    this->renderer = renderer;
}

void Text::set_TTF_Font( std::string file, int ptsize )
{
    TTF_CloseFont( font );
    font = NULL;
    SDL_DestroyTexture( texture );
    texture = NULL;
    width = 0;
    height = 0;

    if( file == "" )
        throw std::runtime_error( name + ": TTF_Font* could not be set! file == ""\n" + SDL_GetError() );
    if( ptsize <= 0 )
        throw std::runtime_error( name + ": TTF_Font* could not be set! ptsize <= 0\n" + SDL_GetError() );

    TTF_Font *font = TTF_OpenFont( file.c_str(), ptsize );

    if( font == NULL )
        throw std::runtime_error( name + ": TTF_Font* could not be set! font == NULL\n" + SDL_GetError() );

    this->font = font;
}

void Text::render_Text_Solid( std::string text, SDL_Color fg )
{
    SDL_DestroyTexture( texture );
    texture = NULL;
    width = 0;
    height = 0;

    SDL_Surface *surface = TTF_RenderText_Solid( font, text.c_str(), fg );

    if( surface == NULL )
        throw std::runtime_error( name + ": Text could not be created! surface == NULL\n" + SDL_GetError() );

    texture = SDL_CreateTextureFromSurface( renderer, surface );
    width = surface->w;
    height = surface->h;
    SDL_FreeSurface( surface );

    if( texture == NULL )
        throw std::runtime_error( name + ": Text could not be created! texture == NULL\n" + SDL_GetError() );
}

Мой Text.h файл:

#ifndef TEXT_H_INCLUDED
#define TEXT_H_INCLUDED

#include <iostream>

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

class Text
{
    public:

    //constructors
    Text();
    Text(SDL_Renderer *renderer, std::string file, int ptsize, std::string name);
    ~Text();

    void set_renderer( SDL_Renderer *renderer );
    void set_TTF_Font( std::string file, int ptsize );
    void render_Text_Solid( std::string text, SDL_Color fg );

    //render
    void render( int x, int y );
    void render( SDL_Rect *srcrect, SDL_Rect *dstrect );

    //variables
    int width = 0;
    int height = 0;

    private:
    SDL_Renderer *renderer = NULL;
    SDL_Texture *texture = NULL;
    TTF_Font *font = NULL;
    std::string name = "";

};

#endif // TEXT_H_INCLUDED

PS : Я использую Manjaro Linux и Codeblocks

1 Ответ

2 голосов
/ 28 мая 2020

Давайте сначала посмотрим, что делают эти строки:

    Text title;
    title = Text( renderer, "cruft.ttf", 32, "title" );

Сначала вы создаете title и инициализируете его конструктором по умолчанию Text::Text(), который устанавливает значения по умолчанию для его полей. Затем вы создаете второй объект Text (назовем его title1 для ясности) со специализированным конструктором. Затем вы копируете title1 в title - поскольку operator= не определено, создается копия по умолчанию. Теперь ваш title имеет те же значения, что и title1. И вызывается title1.~Text(), убивая ваш font.

То, что у вас есть, это ваш title.font, все еще имеющий свое предыдущее значение, но он указывает на уже закрытую структуру шрифтов.

Это могло можно избежать, не создавая временный объект Text - например, Text title = Text(.......) не будет создавать временный объект и копирование. Это просто обходной путь, актуальная проблема остается - ваши объекты Text не могут быть безопасно скопированы. Есть много способов решения этой проблемы - например, использование каких-то оболочек unique_ptr или удаление деструкторов в пользу метода деиницирования вручную и т. Д. c ..

Что приводит нас к следующей проблеме - если вы решите ваше копирование, теперь вы можете сгенерировать текстовую поверхность и текстуру, но посмотрите на свой код выхода:

    Text title;
    // ... omitted
    SDL_Quit();
    TTF_Quit();
    return 0;

У вас есть финализация в обратном порядке - вы хотите закрыть шрифт, удалить текстуры, уничтожить средство визуализации / окно, затем вызовите TTF_Quit и SDL_Quit, но вместо этого у вас есть SDL_Quit(), TTF_Quit(), и только после этого title.~Text() - скорее всего, будет трещать sh, потому что SDL / TTF уже завершен и после этого вы не должны выполнять вызовы SDL. Конечно, это также можно решить, например, заключив ваш title в дополнительный блок кода, но количество вещей, на которые следует обратить внимание, станет слишком большим.

...