vusalg
@vusalg
Студент программист, второй курс

В чем ошибка std::function?

Нужно загружать функции OpenGL и вызывать их через указатель. Все бы ничего, но преподаватель заставил вместо указателя использовать std::function. С ним возникает проблема, вроде все запускается, но работает не корректно.

Минимальный пример:

typedef void ( * PGLCLEARCOLORPROC )  (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);

    std::function <void (GLclampf, GLclampf, GLclampf, GLclampf)> _glClearColor = NULL;

    void glClearColor ( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
    {
        if(_glClearColor == NULL)
            _glClearColor = ( PGLCLEARCOLORPROC ) SDL_GL_GetProcAddress( "glClearColor" );
        else
            _glClearColor (  red,  green,  blue,  alpha );
    }


Весь код:

mygl.h
#ifndef LAB1_HL_S11_V1_MYGL_H
#define LAB1_HL_S11_V1_MYGL_H

typedef unsigned int GLenum;
typedef unsigned int GLbitfield;
typedef unsigned int GLuint;
typedef int GLint;
typedef int GLsizei;
typedef unsigned char GLboolean;
typedef signed char GLbyte;
typedef short GLshort;
typedef unsigned char GLubyte;
typedef unsigned short GLushort;
typedef unsigned long GLulong;
typedef float GLfloat;
typedef float GLclampf;
typedef double GLdouble;
typedef double GLclampd;
typedef void GLvoid;
typedef char GLchar;

#define GL_COLOR_BUFFER_BIT 0x00004000

typedef void ( * PGLCLEARPROC )  (GLbitfield mask);
typedef void ( * PGLCLEARCOLORPROC )  (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);

bool mygl_Init(const char *);
void mygl_Quit();

void glClear(GLbitfield mask);
void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);

#endif //LAB1_HL_S11_V1_MYGL_H


mygl.cpp
#include "mygl.h"
#include <iostream>
#include <functional>
#include <SDL2/SDL.h>

std::function <void (GLclampf, GLclampf, GLclampf, GLclampf)> _glClearColor = NULL;
std::function <void (GLbitfield)> _glClear = NULL;

bool mygl_Init(const char *str)
{
    if (SDL_GL_LoadLibrary(str) < 0)
    {
        std::cerr << "Error in load library" << std::endl;
        return false;
    }

    return true;
}

void mygl_Quit()
{
    SDL_GL_UnloadLibrary();
}

void glClearColor ( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
{
    if(_glClearColor == NULL)
        _glClearColor = ( PGLCLEARCOLORPROC ) SDL_GL_GetProcAddress( "glClearColor" );
    else
        _glClearColor (  red,  green,  blue,  alpha );
}

void glClear (GLbitfield mask)
{
    if( _glClear == NULL) {
        _glClear = (PGLCLEARPROC) SDL_GL_GetProcAddress("glClearColor");
        std::cout << "glClear is NULL" << std::endl;
    }
    else
        _glClear (mask);
}


main.cpp
#include <iostream>
#include "mygl.h"
#include <SDL2/SDL.h>

int main()
{

    SDL_Init(SDL_INIT_EVERYTHING);

    SDL_Event e;
    bool quit = false;

    if (!mygl_Init(NULL))
        return 0;


    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);

    SDL_Window *window = SDL_CreateWindow("Lab 1", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 480, SDL_WINDOW_OPENGL);

    if(!window)
    {
        std::cerr << "Error in create window" << std::endl;
        return 0;
    }

    SDL_GLContext glContext = SDL_GL_CreateContext(window);

    if(!glContext)
    {
        std::cerr << "Error in create gl context" << std::endl;
        return 0;
    }

    while( e.type != SDL_QUIT && !quit)
    {
        while(SDL_PollEvent(&e))
        {
            glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT);

            SDL_GL_SwapWindow(window);
            if(e.key.keysym.sym == SDLK_q && SDL_GetModState() & KMOD_CTRL)
                quit = true;
        }
    }


    SDL_GL_DeleteContext(glContext);
    SDL_DestroyWindow(window);
    mygl_Quit();
    SDL_Quit();
    return 0;
}


Результат работы:

95736fa3111d479c9e9424bec244d8f1.png
  • Вопрос задан
  • 340 просмотров
Решения вопроса 1
AtomKrieg
@AtomKrieg
Давай я поищу в Google за тебя
У меня ваш пример не скомпилировался(ругался на template specialization), но не суть.
Переделал вот так:
#include <iostream>
#include <functional>
#include <SDL.h>
#include <type_traits>

using PGLCLEARPROC = std::add_pointer<void(GLbitfield mask)>::type;
using PGLCLEARCOLORPROC = std::add_pointer<void (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)>::type;

std::function<void (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)>_glClearColor;
std::function<void(GLbitfield mask)> _glClear;

void glClearColor ( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
{
	if(!_glClearColor)
		_glClearColor = static_cast<PGLCLEARCOLORPROC>(SDL_GL_GetProcAddress( "glClearColor" ));

	if (_glClearColor)
		_glClearColor (  red,  green,  blue,  alpha );
}

void glClear (GLbitfield mask)
{
	if(!_glClear)
		_glClear = static_cast<PGLCLEARPROC>(SDL_GL_GetProcAddress("glClear"));			

	if(_glClear)
		_glClear (mask);
}


Здесь описание операторов: www.cplusplus.com/reference/functional/function

Насколько я понимаю косяк заключается в том, что переменные _glClear это не указатели на функцию, а объект. И когда делаете так:
std::function <void (GLbitfield)> _glClear = NULL;
То через std::function::operator=() вы в него загоняете указатель на функцию располагающуюся в NULL. Таким образом инициализируете объект, пусть даже неправильным значением.

А когда вы делаете проверку
if( _glClear == NULL) {
То тут включается в работу оператор std::function::operator bool , которые проверяет что объект инициализирован.
Происходит преобразование типов по такому принципу: NULL -> 0 -> false. Поэтому проверка получается такой: if( _glClear == false) . Но объект уже инициализирован и operator bool возвращает true, поэтому переменная не инициализируется правильным значеним. Дальше происходит call target по адресу NULL - вы его инициалировали ранее и получаете UB.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
vusalg
@vusalg Автор вопроса
Студент программист, второй курс
В классе все работает, но такой вариант мне не подходит, разве что если поля и методы статическими задать, но это не получается

class t {
public:
    bool mygl_Init(const char *);
    void mygl_Quit();
    void glClear(GLbitfield mask);
    void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
private:
    std::function < void (GLbitfield) > _glClear;
    std::function < void (GLclampf, GLclampf, GLclampf, GLclampf) > _glClearColor;
};


typedef void ( * PGLCLEARPROC )  (GLbitfield mask);
typedef void ( * PGLCLEARCOLORPROC )  (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);

bool t::mygl_Init(const char *str)
{
    if (SDL_GL_LoadLibrary(str) < 0)
    {
        return false;
    }

    return true;
}

void t::mygl_Quit()
{
    SDL_GL_UnloadLibrary();
}

void t::glClear ( GLbitfield mask)
{
    if( !_glClear )
        _glClear = (PGLCLEARPROC) SDL_GL_GetProcAddress( "glClear" );
    else
        _glClear (mask);
}
void t::glClearColor ( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
{
    if( !_glClearColor )
        _glClearColor = ( PGLCLEARCOLORPROC ) SDL_GL_GetProcAddress( "glClearColor" );
    else
        _glClearColor (  red,  green,  blue,  alpha );
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы