bindowanie metody klasy na funkcję wolną

0

Witam
Muszę zbindować metodę klasy na void (*fun)(const unsigned, const unsigned) ponieważ funkcja rejestrująca przyjmuje taką sygnaturę, ona jest z innej libki z C i tego nie mogę zmienić. Dla uproszczenia taki przykład sobie napisałem i nie mogę go skompilować:

#include <stdio.h>
#include <iostream>
#include <functional>

bool registerFun(unsigned id, void (*observedFunct)(const unsigned x, const unsigned y)) //taką mam sygnaturę API, którego nie mogę zmienić
{
    return true;
}

class Klasa
{
    public:
    void metoda()
    {
        //najpierw lambdą próbowałem ale też nie działa
        //auto fun = [&](const unsigned x, const unsigned y) -> void
	    //{
    	   // this->observedFunction(x, y);
	    //};
	    using namespace std::placeholders;
	    auto fun = std::bind(&Klasa::observedFunction, this, _1, _2);
        registerFun(1, fun);
    }
    
    void observedFunction(const unsigned x, const unsigned y) //tą funkcje musze zarejestrować jako callback
    {
    }
};

int main()
{
    Klasa().metoda();

    return 0;
}

Wydaje mi się że się nie da czegoś takiego zrobić bo funkcja wolna to funkcja wolna, ma zwracaną wartość, argumenty i tyle a z metodą klasy zawsze pointer na obiekt jest powiązany, więc jak nie mogę zmienić sygnatury rejestrowaczki to nic tu nie zdziałam, mam rację? Musiałbym w rejestrowaczce dodać argument Klasa* ptr., ale rejestrowaczki nie mogę ruszyć.

1

bind to chyba tworzył obiekt funkcyjny, to ci się nie powiedzie z tym rzutem.

lambda, z przechwyceniem np. [this](const unsigned x, const unsigned y) to ci się nie skompiluje nawet.
A to dlatego że tylko lambdę bez przechwycenia można zrzucić do wskaźnika.

Kiedyś chyba widziałem podobne pytanie na stacku. weź rzuć okiem bo możliwe ze na templetach coś wykombinowali.

6
fvg napisał(a):
bool registerFun(unsigned id, void (*observedFunct)(const unsigned x, const unsigned y)) //taką mam sygnaturę API, którego nie

No to kicha. To API nie przestrzega nawet standardów C.
W normalnym C callbacki pisze się razem z kontekstem:

bool registerFun(unsigned id, void (*observedFunct)(const unsigned x, const unsigned y, void* context), void* context)

Zresztą wygląda na to, że API używa stanu globalnego, co czyni kod jeszcze gorszym.
Ergo odradzałbym ufać autorowi tego API. Jeśli masz jakąkolwiek możliwość zamienić to na coś innego, to lepiej to zrobić.


Nie ma wyjścia trzeba robić diabelskie rozwiązania (Evil hack).
Jedyne rozwiązanie to stan globalny (tu ukrywam go pod stałą statyczną p szablonu makeWrappedMethod).

namespace bad_external_api {
typedef void (*CallbackFun)(const unsigned x, const unsigned y);
bool registerFun(unsigned id, void (*observedFunct)(const unsigned x, const unsigned y))
{
    std::cout << "id=" << id << "  fun=" << observedFunct << '\n';
    return true;
}
}
//-----------
namespace api_helper {
template <typename T, T* const* p, void (T::*m)(const unsigned x, const unsigned y)>
void wrappedMethod(const unsigned x, const unsigned y)
{
    ((*p)->*m)(x, y);
}

template <size_t ObjectId = 0, typename T>
bad_external_api::CallbackFun makeWrappedMethod(T* obj)
{
    static T* const p = obj;
    return &wrappedMethod<T, &p, &T::observedFunction>;
}
}
//--------------

class Klasa {
public:
    void metoda()
    {
        bad_external_api::registerFun(1, api_helper::makeWrappedMethod(this));
    }

    void observedFunction(const unsigned x, const unsigned y) // tą funkcje musze zarejestrować jako callback
    {
        std::cout << "x=" << x << " y=" << y << '\n';
    }
};

Na razie dowód, że się kompiluje: https://godbolt.org/z/Yo8qYqn1d

Jest sporo miejsca na usprawnienia.

Wersja z lambdą: https://godbolt.org/z/5ffcnKKvf zwróć uwagę, że to rozwiązanie nie zadziała poprawnie jak ten sam typ lambdy zostanie użyty więcej niż raz (zmienna statyczna p nie będzie ponownie zainicjalizowana).

1 użytkowników online, w tym zalogowanych: 0, gości: 1