Я пытаюсь выполнить программу, в которой создаю очередь packaged_task и пытаюсь выполнить packaged_tasks в отдельном потоке. Я получаю следующую ошибку
Ошибка C2664 'std :: function <_Ret (void)> :: function (std :: function <_Ret (void)> &&)': невозможно преобразовать аргумент 1 из 'std :: _ Binder' в 'std :: nullptr_t'
Код, который у меня есть, показан ниже. Может ли кто-нибудь объяснить, почему возникает эта ошибка?
/*
****************************************************************************************************
The Header File
****************************************************************************************************
*/
class CommandChannel {
private:
std::thread commChannelThread;
std::mutex commChannelmu;
std::condition_variable commChannelcv;
struct taskRequest
{};
struct taskResponse
{};
/* Creates a queue of packaged tasks that accepts one iu */
std::deque< std::packaged_task<int()>> comm_ch_task_queue;
void threadHandler(void);
protected:
int p_impl_theAdd(int a, int b);
int p_impl_theMul(int a, int b);
int p_impl_theDiv(int a, int b);
public:
/*Constructor and the Destructor*/
CommandChannel();
~CommandChannel();
/*The public functions */
int theAdd(int a, int b);
int theMul(int a, int b);
int theDiv(int a, int b);
};
/*
****************************************************************************************************
The Implementation File
****************************************************************************************************
*/
/* Implementation Functions */
int CommandChannel::p_impl_theAdd(int a, int b)
{
return a+b;
}
int CommandChannel::p_impl_theMul(int a, int b)
{
return a*b;
}
int CommandChannel::p_impl_theDiv(int a, int b)
{
return a / b;
}
/* COnstructors and Destructors */
CommandChannel::CommandChannel()
{
/*Creating a new thread that runs the threadHandler function*/
commChannelThread = std::thread(&CommandChannel::threadHandler, this);
}
CommandChannel::~CommandChannel()
{
if (commChannelThread.joinable()) {
commChannelThread.join();
std::cout << "Command Channel Thread Joined " << std::endl;
}
else
std::cout << "Problem in joining the Command Channel Thread" << std ::endl;
}
/* User Public Functions */
int CommandChannel::theAdd(int a, int b)
{
/* Creating the packaged task with the the implementation pointer */
std::packaged_task<int()> t(std::bind(&CommandChannel::p_impl_theAdd, a, b));
/* Pushing the task in the queue */
{
std::lock_guard<std::mutex> locker(commChannelmu);
comm_ch_task_queue.push_back(t);
commChannelcv.notify_one();
}
/* creating the placeholder for the return value */
std::future<int> fu = t.get_future();
/* getting the value from the future */
return fu.get();
}
int CommandChannel::theMul(int a, int b)
{
/* Create the packaged task with the pimpl */
std::packaged_task<int()> t(std::bind(&CommandChannel::p_impl_theMul, a, b));
/* Pushing the task in the queue */
{
std::lock_guard<std::mutex> locker(commChannelmu);
comm_ch_task_queue.push_back(t);
commChannelcv.notify_one();
}
/* Creating the placeholder for the return value */
std::future<int> fu = t.get_future();
/*getting the value from the future*/
return fu.get();
}
int CommandChannel::theDiv(int a, int b)
{
/* Create the packaged tasks with the pimpl */
std::packaged_task<int()> t(std::bind(&CommandChannel::p_impl_theDiv, a, b));
/*Pushing the task in the queue thorigh the mutex locks*/
{
std::lock_guard<std::mutex> locker(commChannelmu);
comm_ch_task_queue.push_back(t);
commChannelcv.notify_one();
}
/* Creating the placeholder for the return value */
std::future<int> fu = t.get_future();
/*getting the value from the future*/
return fu.get();
}
/*
Thread Handler
Pops the elemetns from the queue and then executes them
the value goes to the called function through future references
*/
void CommandChannel::threadHandler()
{
std::packaged_task<int()> t;
{
std::unique_lock<std::mutex> locker(commChannelmu);
commChannelcv.wait(locker);
t = std::move(comm_ch_task_queue.front());
comm_ch_task_queue.pop_front();
}
t();
}
/*
****************************************************************************************************
Main
****************************************************************************************************
*/
int main()
{
CommandChannel api;
api.theAdd(2, 4);
api.theDiv(6, 3);
api.theMul(5, 7);
return 0;
}





В вашем коде есть две ошибки. Во-первых, вы неправильно используете свои папки. При привязке функции-члена первый аргумент должен иметь указатель на класс, в вашем случае this. Вот один пример фиксированного кода:
std::packaged_task<int()> t(std::bind(&CommandChannel::p_impl_theDiv, this, a, b));
Однако использование лямбда-выражений обычно гораздо более интуитивно понятно и синтаксически дружественно, чем связыватели. Вот та же идея, выраженная с помощью лямбды:
std::packaged_task<int()> t([this, a, b]() {return p_impl_theDiv(a, b);});
Кроме того, как только это будет исправлено повсюду, у вас возникнет другая проблема - вы копируете объекты packaged_task, помещая их в очередь. Эти объекты нельзя скопировать, вместо этого вам нужно переместить. Опять же, вот один пример этого исправления:
comm_ch_task_queue.push_back(std::move(t));
С этим кодом могут быть другие проблемы, я исправил только ошибки компиляции.
Вы связываете нестатическую функцию-член класса,
поэтому вам нужно указать объект, для которого
вызывается функция-член. this отсутствует
во всех звонках bind:
std::packaged_task<int()> t(std::bind(&CommandChannel::p_impl_theDiv, this, a, b));
сделайте то же самое для theMul и theAdd.
Вторая проблема, объект packaged_task не копируется,
если вы хотите передать этот объект как параметр
push_back вы должны кастовать объект packaged_task
в ссылку Rvalue, то packaged_task будет перемещен
в вектор.
comm_ch_task_queue.push_back(std::move(t));
Все вышесказанное сделано для устранения ошибок при компиляции.
1)
Поскольку packaged_task перемещается методом theDiv,
вы не можете вызывать какие-либо члены для этого объекта после выполнения операции перемещения.
После нажатия packaged_task вы должны выйти из функции theDiv.
void CommandChannel::theDiv(int a, int b)
{
/* Create the packaged tasks with the pimpl */
std::packaged_task<int()> t(std::bind(&CommandChannel::p_impl_theDiv, this, a, b));
/*Pushing the task in the queue thorigh the mutex locks*/
{
std::lock_guard<std::mutex> locker(commChannelmu);
comm_ch_task_queue.push_back(std::move(t));
commChannelcv.notify_one();
}
// access to t is forbidden
}
Сделайте то же самое с методами theAdd и theMul.
2) Ваша функция потока выполняет только одну задачу из очереди. Думаю, там какой-то шлейф отсутствует. Вы хотите выполнить все поступающие задачи в очередь (подробности в комментариях):
void CommandChannel::threadHandler()
{
std::packaged_task<int()> t;
while (true) // inifite loop to process incoming tasks
{
std::unique_lock<std::mutex> locker(commChannelmu);
commChannelcv.wait(locker,[this](){ return comm_ch_task_queue.size();});
// wait returns if there is some task to be performed
t = std::move(comm_ch_task_queue.front());
comm_ch_task_queue.pop_front();
std::future<int> f = t.get_future();
t(); // perform task
std::cout << f.get() << std::endl; // print value
}
}
@ rafixx07 В этой реализации функция theAdd (theMul и theDiv тоже) не возвращает никакого значения. Что, если я хочу обменять возвращаемое значение из обработчика потока на функцию theAdd?
@D_wanna_B_coder Если я правильно понимаю: вы хотите создать задачу, поместить задачу в очередь и подождать в theAdd, пока задача не будет выполнена в потоке Handler, затем прочитать значение из будущего и вернуться из Add? Для этого вам нужно вызвать std::future<int> fu = t.get_future(); перед {LOCK; comm_ch_task_queue.push_back(move(t)); NOTIFY_ONE}: так что вы получите future, packaged_task перемещается в очередь, затем вы ждете, пока общее состояние будет готово в будущем. В Handler вам нужно только прочитать packaged_task, вызвать () для задачи и удалить задачу из очереди. Тогда в Handler нельзя использовать future.
Это именно то, что я хотел, и теперь код работает.
не используйте привязку, вместо этого используйте лямбда.