从静态方法调用非静态附加时,使用 std::packaged_task 将 CAsyncSocket-Detach-socket 任务排队会导致编译错误
Using std::packaged_task to queue CAsyncSocket-Detach-socket tasks causes compile error when non-static Attach is called from static method
我正在实现代码,这样我就可以在网络连接到达时接受它们,将它们从到达的套接字中分离出来,创建一个 std::packaged_task 任务,将该任务放入双端队列容器中,然后在它们的队列中执行这些任务任务线程稍后。 Bo Qian 关于“C++ 线程 #9:packaged_task”的 YouTube 讲座让这一切变得简单,其中展示了如何做到这一点。
#include "stdafx.h"
#include <afxsock.h>
#include <condition_variable>
#include <deque>
#include <future>
std::condition_variable notifyDequeNotEmptyCondVar;
std::mutex decodeMu;
class MyRxDecode : public CAsyncSocket
{
public:
static std::deque< std::packaged_task< bool() > > rxAcceptedTasks;
static bool StartDecode( SOCKET socket )
{
bool result = true;
// Attach detached socket to this socket
//result = Attach( socket ); // error C2352: 'CAsyncSocket::Attach': illegal call of non-static member function
return result;
}
static bool DecodeTaskThread()
{
std::packaged_task< bool() > DecodingTask;
{
std::unique_lock< std::mutex > dequeLocker( decodeMu ); // makes sure all deque actions are atomic
notifyDequeNotEmptyCondVar.wait( dequeLocker, [] () { return !rxAcceptedTasks.empty(); } ); // wait until notified that deque is not empty
DecodingTask = std::move( rxAcceptedTasks.front() );
rxAcceptedTasks.pop_front();
}
DecodingTask(); // has no arg because the arg was previously bound to the functor passed in
return true;
}
};
class MyListener : CAsyncSocket
{
virtual void OnAccept( int nErrorCode ) // is called when other socket does a connect on this socket's endpoint
{
CAsyncSocket syncSocket; // msdn prescribes creating stack socket
if( Accept( syncSocket ) )
{
AsyncSelect( FD_READ | FD_CLOSE ); // msdn
SOCKET socket = syncSocket.Detach(); // msdn
// Bo Qian's lecture explains how this packaged task code works and is made thread safe.
// Create task in separate thread to process this connection and push onto deque. The main advantage of a packaged task compared to using a functor is the former links the callable object to a future, which is useful in a multi-threaded environment (Bo Qian).
std::thread decodeThread( MyRxDecode::DecodeTaskThread ); // pass-by-value ctor
std::packaged_task< bool() > rxAcceptTask( std::bind( MyRxDecode::StartDecode, socket ) ); // binds function with its param to create functor wh is passed to packaged task's ctor
std::future< bool > rxAcceptTaskFuture = rxAcceptTask.get_future();
{
std::lock_guard< std::mutex > locker( decodeMu );
MyRxDecode::rxAcceptedTasks.push_back( std::move( rxAcceptTask ) );
}
notifyDequeNotEmptyCondVar.notify_one();
bool taskResult = rxAcceptTaskFuture.get();
decodeThread.join();
CAsyncSocket::OnAccept( nErrorCode ); // msdn
}
}
};
std::deque< std::packaged_task< bool() > > MyRxDecode::rxAcceptedTasks;
int main()
{
return 0;
}
代码在我的例子中没有编译,因为我的 StartDecode 是一个试图调用非静态 Attach 的静态方法。 StartDecode 是一个静态方法,因为 std::bind 用于将“套接字”绑定到任务。 ‘socket’通常会传入StartDecode方法,但为了使打包任务中的'future'正常工作,任何传入的参数都必须使用std::bind提前绑定。但是一旦 StartDecode 变为静态,调用非静态的 CAsyncSocket 的 Attach 将导致错误 C2352。
如何从静态 MyRxDecode::StartDecode 调用非静态 Attach 方法?有没有办法避免将套接字参数绑定到任务而不使其成为静态?
std::bind 允许您调用非静态成员函数。参见 cppreference.com->std::bind:
Notes
… when invoking a pointer to non-static member function or pointer to
non-static data member, the first argument has to be a reference or pointer (including,
possibly, smart pointer such as std::shared_ptr and std::unique_ptr) to an object whose
member will be accessed.
用以下代码替换上面代码中 rxAcceptTask 的定义:
std::packaged_task< bool() > rxAcceptTask( std::bind( &MyRxDecode::StartDecode, &myRxDecode, socket ) );
让 bool StartDecode( SOCKET socket ) 成员函数变为非静态。
我正在实现代码,这样我就可以在网络连接到达时接受它们,将它们从到达的套接字中分离出来,创建一个 std::packaged_task 任务,将该任务放入双端队列容器中,然后在它们的队列中执行这些任务任务线程稍后。 Bo Qian 关于“C++ 线程 #9:packaged_task”的 YouTube 讲座让这一切变得简单,其中展示了如何做到这一点。
#include "stdafx.h"
#include <afxsock.h>
#include <condition_variable>
#include <deque>
#include <future>
std::condition_variable notifyDequeNotEmptyCondVar;
std::mutex decodeMu;
class MyRxDecode : public CAsyncSocket
{
public:
static std::deque< std::packaged_task< bool() > > rxAcceptedTasks;
static bool StartDecode( SOCKET socket )
{
bool result = true;
// Attach detached socket to this socket
//result = Attach( socket ); // error C2352: 'CAsyncSocket::Attach': illegal call of non-static member function
return result;
}
static bool DecodeTaskThread()
{
std::packaged_task< bool() > DecodingTask;
{
std::unique_lock< std::mutex > dequeLocker( decodeMu ); // makes sure all deque actions are atomic
notifyDequeNotEmptyCondVar.wait( dequeLocker, [] () { return !rxAcceptedTasks.empty(); } ); // wait until notified that deque is not empty
DecodingTask = std::move( rxAcceptedTasks.front() );
rxAcceptedTasks.pop_front();
}
DecodingTask(); // has no arg because the arg was previously bound to the functor passed in
return true;
}
};
class MyListener : CAsyncSocket
{
virtual void OnAccept( int nErrorCode ) // is called when other socket does a connect on this socket's endpoint
{
CAsyncSocket syncSocket; // msdn prescribes creating stack socket
if( Accept( syncSocket ) )
{
AsyncSelect( FD_READ | FD_CLOSE ); // msdn
SOCKET socket = syncSocket.Detach(); // msdn
// Bo Qian's lecture explains how this packaged task code works and is made thread safe.
// Create task in separate thread to process this connection and push onto deque. The main advantage of a packaged task compared to using a functor is the former links the callable object to a future, which is useful in a multi-threaded environment (Bo Qian).
std::thread decodeThread( MyRxDecode::DecodeTaskThread ); // pass-by-value ctor
std::packaged_task< bool() > rxAcceptTask( std::bind( MyRxDecode::StartDecode, socket ) ); // binds function with its param to create functor wh is passed to packaged task's ctor
std::future< bool > rxAcceptTaskFuture = rxAcceptTask.get_future();
{
std::lock_guard< std::mutex > locker( decodeMu );
MyRxDecode::rxAcceptedTasks.push_back( std::move( rxAcceptTask ) );
}
notifyDequeNotEmptyCondVar.notify_one();
bool taskResult = rxAcceptTaskFuture.get();
decodeThread.join();
CAsyncSocket::OnAccept( nErrorCode ); // msdn
}
}
};
std::deque< std::packaged_task< bool() > > MyRxDecode::rxAcceptedTasks;
int main()
{
return 0;
}
代码在我的例子中没有编译,因为我的 StartDecode 是一个试图调用非静态 Attach 的静态方法。 StartDecode 是一个静态方法,因为 std::bind 用于将“套接字”绑定到任务。 ‘socket’通常会传入StartDecode方法,但为了使打包任务中的'future'正常工作,任何传入的参数都必须使用std::bind提前绑定。但是一旦 StartDecode 变为静态,调用非静态的 CAsyncSocket 的 Attach 将导致错误 C2352。
如何从静态 MyRxDecode::StartDecode 调用非静态 Attach 方法?有没有办法避免将套接字参数绑定到任务而不使其成为静态?
std::bind 允许您调用非静态成员函数。参见 cppreference.com->std::bind:
Notes
… when invoking a pointer to non-static member function or pointer to
non-static data member, the first argument has to be a reference or pointer (including,
possibly, smart pointer such as std::shared_ptr and std::unique_ptr) to an object whose
member will be accessed.
用以下代码替换上面代码中 rxAcceptTask 的定义:
std::packaged_task< bool() > rxAcceptTask( std::bind( &MyRxDecode::StartDecode, &myRxDecode, socket ) );
让 bool StartDecode( SOCKET socket ) 成员函数变为非静态。