Как передать функции-члены в g_source_set_callback? - PullRequest
0 голосов
/ 27 февраля 2020

Я пытаюсь передать указатели на функции-члены в g_source_set_callback, чтобы поставить их в очередь. Это фиктивный код, который я написал.

#include<glib.h>

#include<iostream>
#include<thread>
#include<chrono>

using namespace std;
using namespace std::chrono_literals;

class executor
{
private:
    GMainLoop* main_loop;
    GMainContext* worker_context;
    thread worker_thread;

    void worker_loop()
    {
        g_main_context_push_thread_default(worker_context);
        cout << "Starting main loop" << endl;
        g_main_loop_run(main_loop);
        cout << "Finished main loop" << endl;
        g_main_context_pop_thread_default(worker_context);
    }

    void queue_callback(int (*callback)(void))
    {
        GSource* idle_source = g_idle_source_new();
        g_source_set_callback(idle_source, (GSourceFunc)callback, NULL, NULL);
        g_source_attach(idle_source, worker_context);
        g_source_unref(idle_source);
    }


    int func1()
    {
        cout << "func1 started" << endl;
        this_thread::sleep_for(5s);
        cout << "func1 finished waiting" << endl;
        return 0;
    }

    int func2()
    {
        cout << "func2 started" << endl;
        this_thread::sleep_for(1s);
        cout << "func2 finished waiting" << endl;
        return 0;
    }

public:
    executor()
    {
        worker_context = g_main_context_new();
        main_loop = g_main_loop_new(worker_context, false);
        worker_thread = thread(&executor::worker_loop, this);
    }

    ~executor()
    {
        cout << "Stopping main loop" << endl;
        GSource* idle_source = g_idle_source_new();
        g_source_set_callback(idle_source, (GSourceFunc)g_main_loop_quit, main_loop, NULL);
        g_source_attach(idle_source, worker_context);
        g_source_unref(idle_source);

        if (worker_thread.joinable())
        {
            worker_thread.join();
        }
        cout << "Removing references to main loop and context" << endl;

        g_main_loop_unref(main_loop);
        g_main_context_unref(worker_context);
    }

    void start()
    {
        queue_callback(func1);
        queue_callback(func2);
    }
};

int main()
{
    executor e;
    e.start();
    return 0;
}

Я знаю, что вы не можете передать функцию-член c, не относящуюся к состоянию, таким образом, поэтому я получаю следующую ошибку компиляции, как и ожидалось.

test.cpp: In member function ‘void executor::start()’:
test.cpp:79:37: error: invalid use of non-static member function ‘int executor::func1()’
                 queue_callback(func1);
                                     ^
test.cpp:35:13: note: declared here
         int func1()
             ^~~~~
test.cpp:80:37: error: invalid use of non-static member function ‘int executor::func2()’
                 queue_callback(func2);
                                     ^
test.cpp:43:13: note: declared here
         int func2()
             ^~~~~

Я видел, как кто-то использовал функциональные объекты, чтобы обернуть функции-члены. Итак, я попробовал это.

#include<glib.h>

#include<iostream>
#include<thread>
#include<chrono>
#include<functional>

using namespace std;
using namespace std::chrono_literals;

class executor
{
private:
    GMainLoop* main_loop;
    GMainContext* worker_context;
    thread worker_thread;

    void worker_loop()
    {
        g_main_context_push_thread_default(worker_context);
        cout << "Starting main loop" << endl;
        g_main_loop_run(main_loop);
        cout << "Finished main loop" << endl;
        g_main_context_pop_thread_default(worker_context);
    }

    void queue_callback(const function<int()>* callback)
    {
        GSource* idle_source = g_idle_source_new();
        g_source_set_callback(idle_source, reinterpret_cast<GSourceFunc>(&on_callback), const_cast<function<int()>*>(callback), NULL);
        g_source_attach(idle_source, worker_context);
        g_source_unref(idle_source);
    }

    static int on_callback(const function<int()>* callback)
    {
        return (*callback)();
    }

    int func1()
    {
        cout << "func1 started" << endl;
        this_thread::sleep_for(5s);
        cout << "func1 finished waiting" << endl;
        return 0;
    }

    int func2()
    {
        cout << "func2 started" << endl;
        this_thread::sleep_for(1s);
        cout << "func2 finished waiting" << endl;
        return 0;
    }

public:
    executor()
    {
        worker_context = g_main_context_new();
        main_loop = g_main_loop_new(worker_context, false);
        worker_thread = thread(&executor::worker_loop, this);
    }

    ~executor()
    {
        cout << "Stopping main loop" << endl;
        GSource* idle_source = g_idle_source_new();
        g_source_set_callback(idle_source, (GSourceFunc)g_main_loop_quit, main_loop, NULL);
        g_source_attach(idle_source, worker_context);
        g_source_unref(idle_source);

        if (worker_thread.joinable())
        {
            worker_thread.join();
        }
        cout << "Removing references to main loop and context" << endl;

        g_main_loop_unref(main_loop);
        g_main_context_unref(worker_context);
    }

    void start()
    {
        cout << "Submitting func1 callback" << endl;
        function<int()> callback1 = [this]() {
            return func1();
        };
        queue_callback(&callback1);

        cout << "Submitting func2 callback" << endl;
        function<int()> callback2 = [this]() {
            return func2();
        };
        queue_callback(&callback2);
    }
};

int main()
{
    executor e;
    e.start();
    return 0;
}

Этот код компилируется, но я получаю либо ошибку сегментации, либо исключение bad_function_call.

Starting main loop
Submitting func1 callback
Submitting func2 callback
Stopping main loop
func1 started
func1 finished waiting
terminate called after throwing an instance of 'std::bad_function_call'
  what():  bad_function_call
Aborted (core dumped)

Я думаю, что получаю ошибку, потому что callback1 и callback2 являются локальными объектами, и к тому времени, когда они выполняются, память для них освобождается.

Как это исправить? Я думал об использовании unique_ptr s, но не мог понять, как g_source_set_callback принимает int (*GSourceFunc) void в качестве второго аргумента и void* в качестве третьего.

1 Ответ

0 голосов
/ 28 февраля 2020

Я понял это. Я создаю новый std::function объект и передаю его g_source_set_callback. Когда вызывается on_callback, я печатаю от void * до std::function<int()>*, вызываю его и удаляю. Вот рабочий код.

#include<glib.h>

#include<iostream>
#include<thread>
#include<chrono>
#include<functional>

using namespace std;
using namespace std::chrono_literals;

class executor
{
private:
    GMainLoop* main_loop;
    GMainContext* worker_context;
    thread worker_thread;

    void worker_loop()
    {
        g_main_context_push_thread_default(worker_context);
        cout << "Starting main loop" << endl;
        g_main_loop_run(main_loop);
        cout << "Finished main loop" << endl;
        g_main_context_pop_thread_default(worker_context);
    }

    void queue_callback(function<int()>&& callback)
    {
        GSource* idle_source = g_idle_source_new();
        g_source_set_callback(idle_source, on_callback, new function<int()>(callback), NULL);
        g_source_attach(idle_source, worker_context);
        g_source_unref(idle_source);
    }

    static int on_callback(void* ptr)
    {
        function<int()>* callback = static_cast<function<int()>*>(ptr);
        int ret = (*callback)();
        delete callback;
        return ret;
    }

    int func1()
    {
        cout << "func1 started" << endl;
        this_thread::sleep_for(5s);
        cout << "func1 finished waiting" << endl;
        return 0;
    }

    int func2()
    {
        cout << "func2 started" << endl;
        this_thread::sleep_for(1s);
        cout << "func2 finished waiting" << endl;
        return 0;
    }

public:
    executor()
    {
        worker_context = g_main_context_new();
        main_loop = g_main_loop_new(worker_context, false);
        worker_thread = thread(&executor::worker_loop, this);
    }

    ~executor()
    {
        cout << "Stopping main loop" << endl;
        GSource* idle_source = g_idle_source_new();
        g_source_set_callback(idle_source, (GSourceFunc)g_main_loop_quit, main_loop, NULL);
        g_source_attach(idle_source, worker_context);
        g_source_unref(idle_source);

        if (worker_thread.joinable())
        {
            worker_thread.join();
        }
        cout << "Removing references to main loop and context" << endl;

        g_main_loop_unref(main_loop);
        g_main_context_unref(worker_context);
    }

    void start()
    {
        cout << "Submitting func1 callback" << endl;
        queue_callback([this]() { return func1(); });

        cout << "Submitting func2 callback" << endl;
        queue_callback([this]() { return func2(); });
    }
};

int main()
{
    executor e;
    e.start();
    return 0;
}
...