Problem ze wskaźnikami na funkcje

0

(VS2017, GLFW, GLAD, OpenGL)

Posiadam taką klasę:

class Klasa
{
private:
    //składowe prywatne
public:
    void framebuffer_size_callback(GLFWwindow* win, int w, int h);
};

Definicja void framebuffer_size_callback(GLFWwindow* win, int w, int h);

void framebuffer_size_callback(GLFWwindow* win, int w, int h)
{
    glViewport(0, 0, w, h)
}

Następnie w innym fragmencie kodu chcę przekazać wskaźnik na tą funkcję do innej funkcji:

    glfwSetFramebufferSizeCallback(win /* jakaś zmienna wcześniej zadeklarowana*/, this->framebuffer_size_callback /* przy samym 
    framebuffer_size_callback wyskakuje inny błąd*/);

IntelliSense się przymyka, ale jak zaczynam kompilację, wyskakuje błąd:
"niestandardowa składnia; użyj znaku „&”, aby utworzyć wskaźnik do składowej"

O co chodzi? Funkcja framebuffer_size_callback() musi być składową klasy Klasa ponieważ korzysta z jej składowych, (niekoniecznie prywatnych) a przez parametry referencji do obiektu przekazać nie mogę bo mam ściśle określony prototyp.

0

Masz użyć

&Klasa::framebuffer_size_callback 

Przy czym jest to wskaźnik typu

void (Klasa::*)(GLFWwindow*, int, int)

a twoje api pewnie oczekuje

void(*)(GLFWwindow*, int, int)
0

Teraz kompilator krzyczy: GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow *,GLFWframebuffersizefun)”: nie można dokonać konwersji argumentu 2 z „void (__cdecl Klasa::* )(GLFWwindow *,int,int)” do „GLFWframebuffersizefun",

przy czym GLFWframebuffersizefun to: typedef void (*GLFWframebuffersizefun)(GLFWwindow*, int, int)

0

Czyli to co pisałem. Nie możesz przekazać niestatycznej funkcji klasy, bo to inny typ. Zmień ją w funkcję statyczną lub użyj funkcji wolnej.

0

Statyczna być nie może, bo metoda musi mieć dostęp do danych związanych z obiektem, nie z klasą. Z funkcją wolną tak samo. Jest jakiś sposób na przekazanie obiektu do tej funkcji inaczej niż przez argumenty?

0

Ustaw na swoim oknie user pointer za pomocą glfwSetWindowUserPointer(), a potem w callbacku go odzyskaj używając glfwGetWindowUserPointer()
https://www.glfw.org/docs/3.3/group__window.html#ga3d2fc6026e690ab31a13f78bc9fd3651

0

"C++ have pointers to functions, but C++ programmers do not use them if they are sane" Uncle bob

Jednak to glfwSetFramebufferSizeCallback to jest API C https://www.glfw.org/docs/latest/group__window.html#gab3fb7c3366577daef18c0023e2a8591f

Teraz twoje framebuffer_size_callback to jest metoda, a nie funkcja! Efekt jest taki, że jako funkcja ma dodatkowy ukryty parametr this i dlatego nie może być być skonwertowana do typu akceptowanego przez glfwSetFramebufferSizeCallback.

Najprostsze rozwiązanie to uczynić framebuffer_size_callback metodą statyczną (brak parametry this).
Problemy zaczynają się, jeśli chcesz mieć dostęp do pól obiektu twojej klasy Klasa. Niestety glfwSetFramebufferSizeCallback nie dostarcza standardowego rozwiązania w postaci dowolnego kontekstu.

Jedyne znane mi rozwiązanie, jest brzydkie i straszne, dodać stan globalny, który zmapuje wskaźnik GLFWwindow * do wskaźnika twojej klasy.

Inne mniej paskudne, to niech twoja klasa dziedziczy po GLFWwindow, wtedy konwersja będzie możliwa:

class Klasa : public GLFWwindow
{
public:
      void framebuffer_size_callback(int, int); // może być potrzebne extern "C"  lub __cdecl
      static void framebuffer_size_callback2(GLFWwindow *self, int x, int  y) {
             static_cast<Klasa>(self)->framebuffer_size_callback(x, y);
      }

      void registerCallback() {
             glfwSetFramebufferSizeCallback(this, &Klasa::framebuffer_size_callback);
             //albo
             glfwSetFramebufferSizeCallback(this, &Klasa::framebuffer_size_callback2);
      }
};


0

Nie za bardzo rozumiem jak miałoby mi to pomóc w rozwiązaniu mojego problemu. Mógłbyś mi to bardziej wytłumaczyć? Uczę się właśnie OpenGLa.

2

glfwSetFramebufferSizeCallback nie oferuje możliwości przekazywania kontekstu. Jedyne miejsce, gdzie możesz to zrobić to obiekt GLFWindow, więc będzie to wyglądało jakoś tak:

glfwSetWindowUserPointer(win, this);
glfwSetFramebufferSizeCallback(win, &Klasa::framebuffer_size_callback);

A sama funkcja musi być wolna/statyczna:

/* static */
void Klasa::framebuffer_size_callback(GLFWwindow* win, int w, int h)
{
    Klasa* this_ = static_cast<Klasa*>(glfwGetWindowUserPointer(win));
    glViewport(0, 0, w, h)
}
0

Czyli funkcja glfwSetWindowUserPointer ustawia mi coś w rodzaju takiego kontenera na moje dane i GLFW ignoruje jego zawartość?

0

Nie, ta funkcja ustawia callback na operację resize okna. Przecież to jest napisane w dokumentacji.

0

WP polecenie:

#include <iostream>
using namespace std;

class Klasa
{
	private:
    int x;
	public:
	Klasa(int x):x(x){}
    void callback(int w, int h)
    {
    	cout<<x<<endl;
    }
};

class Caller
{
	private:
	Klasa *klasa;
	public:
	Caller(Klasa *klasa):klasa(klasa) {}
	void call(int w, int h) { klasa->callback(w,h); }
};


int main()
{
	Klasa k(13);
	Caller caller(&k);
	caller.call(666,666);
	return 0;
}
0

Lepiej późno niż wcale :trollface:

#include <cstdio>
#include <cstdlib>
#include <memory>
#include <cstring>

typedef unsigned char* uint8_t;

class Klass {
    public:
        Klass(int value): _value(value) { }

        void set_value(int value);

        void print_value() {
            printf("value = %d\n", this->_value);
        }

    private:
        int _value;
};

void thunk__Klass__set_value(int value) {
    Klass* this_ptr = (Klass*) 0x11aabbccddeeff11;
    this_ptr->set_value(value);
}

// Checked using disassembler/Ghidra
#define THUNK_SIZE (0x6f6 - 0x6ca + 1 + 100)

// This MUST be put after thunk (or in diff file) - to force linker to use full addresses (instead of relative)
// in CALL op-code
void Klass::set_value(int value) { this->_value = value; }

typedef void (*fptr_t)(int);

int main() {
    Klass klass(7);

    klass.set_value(107);
    klass.print_value();

    // Create in-memory copy of thunk__Klass__set_value
    uint8_t* mem = (uint8_t *) malloc(THUNK_SIZE+1); // Will be aligned
    if (mem == 0) {
        printf("error: cannot malloc");
        exit(1);
    }
    memcpy((void *)mem, (void *)&thunk__Klass__set_value, THUNK_SIZE);
    mem[THUNK_SIZE] = 0x00;    

    printf("overwrite placeholder\n");
    Klass** tmp = (Klass**) strstr((char*)mem, "\x11\xff\xee\xdd\xcc\xbb\xaa");
    if (tmp == 0) {
        printf("error: cannot find placeholder\n");
        exit(2);
    }
    else {
        *tmp = &klass;
    }
   
    // Voila we have our own custom thunk that will call class method
    fptr_t fptr = (fptr_t)mem;

    fptr(33);
    printf("after call\n");
    klass.print_value();

    fptr(42);
    printf("after call\n");
    klass.print_value();

    return 0;
}

Skompilowane na Linuxie:

gcc  -fno-stack-protector -z execstack main.cpp

Daje:

value = 107
overwrite placeholder
after call
value = 33
after call
value = 42

:trollface:

1

@0xmarcin nie wiem co to ma udowadniać?
Twój kod zadziała, tylko dla konkretnego kompilatora i na dodatek dla konkretnych opcji kompilatora.
Jako, że thunk__Klass__set_value jest klasycznym przykładem Undefined Behavior, kompilator ma prawo usunąć cały ten kod.

Ani nie jest to "pro", ani pomocne, tylko mąci w głowie początkującemu. Mi to wygląda na nieudaną próbę "szpanu".

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.