Search code examples
c++c++11boostboost-asioboost-bind

Boost.Bind'ing a member function and posting it to io_service


I am trying to wrap an object that represents a job to be done by an io_service.

The job is of arbitrary type, and does not have to be an IO operation. Similar to what is described here.

I have been able to post bound regular functions, but was not able to post member functions.

Why this code does not compile:

#include <iostream>
#include "boost/asio.hpp"
#include "boost/thread.hpp"

using namespace std;
namespace asio = boost::asio;

class class_fun1 {
public:

    void an_expensive_calculation(int num) {
        cout << "an_expensive_calculation: " << num << endl;
    }
};

class class_fun2 {
public:
    void a_long_running_task(int num) {
        for (int x = 0; x < num; ++x)
            cout << "a_long_running_task: " << num << endl;
    }
};

int main(int argc, char** argv) {

    int my_thread_count = 4;

    asio::io_service io_service;
    asio::io_service::work work(io_service);

    boost::thread_group threads;
    for (std::size_t i = 0; i < my_thread_count; ++i)
        threads.create_thread(boost::bind(&asio::io_service::run, &io_service));

    class_fun1 f1();
    class_fun2 f2();
    io_service.post(boost::bind(&class_fun1::an_expensive_calculation, &f1, 42));
    io_service.post(boost::bind(&class_fun2::a_long_running_task, &f2, 123));

    threads.join_all();

    return 0;
}

while this one works:

#include <iostream>
#include "boost/asio.hpp"
#include "boost/thread.hpp"

using namespace std;
namespace asio = boost::asio;

void an_expensive_calculation(int num) {
    cout << "an_expensive_calculation: " << num << endl;
}

void a_long_running_task(int num) {
    for (int x = 0; x < num; ++x)
        cout << "a_long_running_task: " << num << endl;
}


int main(int argc, char** argv) {

    int my_thread_count = 4;

    asio::io_service io_service;
    asio::io_service::work work(io_service);

    boost::thread_group threads;
    for (std::size_t i = 0; i < my_thread_count; ++i)
        threads.create_thread(boost::bind(&asio::io_service::run, &io_service));

    io_service.post(boost::bind(an_expensive_calculation, 42));
    io_service.post(boost::bind(a_long_running_task, 123));

    threads.join_all();

    return 0;
}

I went through some of the online tutorials and documentation, and as far as I know that first should work. I followed the guidelines for binding a member function and posting it to io_service, but it did not work.


Solution

  • The issue is the result of the most vexing parse. In particular, the following declares two functions:

    class_fun1 f1(); // function declaration
    class_fun2 f2(); // function declaration
    

    The first declares a function named f1 that takes no arguments and returns an instance of class_func1. It does not declare an instance of class_func1 with an identifier of f1. A similar situation holds true for f2.

    To resolve this, remove the parenthesis, changing the code to:

    class_fun1 f1; // declares a variable
    class_fun2 f2; // declares a variable
    

    Given clang's compiler output messages, sometimes it can be good to turn up the compiler warning and try to compile with it. In particular, when trying to parse the original code with clang, it provides some helpful output:

    main.cpp:35:18: error: empty parentheses interpreted as a function declaration [-Werror,-Wvexing-parse]
        class_fun1 f1();
                     ^~
    main.cpp:35:18: note: remove parentheses to declare a variable
        class_fun1 f1();
    

    Also, due to the boost::asio::work object's lifetime, the program will never terminate, as the thread group will never be successfully joined. To resolve this, consider destroying the work object before joining the thread group or posting work into the io_service before running it. For more details on when the io_service will block and unblock, consider reading this question.