我们已经在之前的文章中对WeaselServer.cpp中的代码及其功能进行了分析,接下来将对WeaselServerImpl.cpp文件进行分析,首先具体的代码如下:
#include "stdafx.h"
#include "WeaselServerImpl.h"
#include <Windows.h>
#include <VersionHelpers.hpp>
#include <resource.h>
namespace weasel {
class PipeServer : public PipeChannel<DWORD, PipeMessage>
{
public:
using ServerRunner = std::function<void()>;
using Respond = std::function<void(Msg)>;
using ServerHandler = std::function<void(PipeMessage, Respond)>;
PipeServer(std::wstring &&pn_cmd, SECURITY_ATTRIBUTES *s);
public:
void Listen(ServerHandler const &handler);
/* Get a server runner */
ServerRunner GetServerRunner(ServerHandler const &handler);
private:
void _ProcessPipeThread(HANDLE pipe, ServerHandler const &handler);
};
}
using namespace weasel;
extern CAppModule _Module;
ServerImpl::ServerImpl()
: m_pRequestHandler(NULL),
channel(std::make_unique<PipeServer>(GetPipeName(), sa.get_attr()))
{
m_hUser32Module = GetModuleHandle(_T("user32.dll"));
}
ServerImpl::~ServerImpl()
{
_Finailize();
}
void ServerImpl::_Finailize()
{
if (pipeThread != nullptr) {
pipeThread->interrupt();
}
if (IsWindow())
{
DestroyWindow();
}
m_pRequestHandler->Finalize();
}
LRESULT ServerImpl::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// not neccessary...
::SetWindowText(m_hWnd, WEASEL_IPC_WINDOW);
return 0;
}
LRESULT ServerImpl::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
Stop();
return 0;
}
LRESULT ServerImpl::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled = FALSE;
return 1;
}
LRESULT ServerImpl::OnQueryEndSystemSession(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
return TRUE;
}
LRESULT ServerImpl::OnEndSystemSession(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if (m_pRequestHandler)
{
m_pRequestHandler->Finalize();
}
return 0;
}
LRESULT ServerImpl::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
UINT uID = LOWORD(wParam);
switch (uID) {
case ID_WEASELTRAY_ENABLE_ASCII:
m_pRequestHandler->SetOption(lParam, "ascii_mode", true);
return 0;
case ID_WEASELTRAY_DISABLE_ASCII:
m_pRequestHandler->SetOption(lParam, "ascii_mode", false);
return 0;
default:;
}
std::map<UINT, CommandHandler>::iterator it = m_MenuHandlers.find(uID);
if (it == m_MenuHandlers.end())
{
bHandled = FALSE;
return 0;
}
it->second(); // execute command
return 0;
}
DWORD ServerImpl::OnCommand(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
BOOL handled = TRUE;
OnCommand(uMsg, wParam, lParam, handled);
return handled;
}
int ServerImpl::Start()
{
std::wstring instanceName = L"(WEASEL)Furandōru-Sukāretto-";
instanceName += getUsername();
HANDLE hMutexOneInstance = ::CreateMutex(NULL, FALSE, instanceName.c_str());
bool areYouOK = (::GetLastError() == ERROR_ALREADY_EXISTS ||
::GetLastError() == ERROR_ACCESS_DENIED);
if (areYouOK) {
return 0; // assure single instance
}
HWND hwnd = Create(NULL);
return (int)hwnd;
}
int ServerImpl::Stop()
{
_Finailize();
// quit the server
::ExitProcess(0);
return 0;
}
int ServerImpl::Run()
{
// This workaround causes a VC internal error:
// void PipeServer::Listen(ServerHandler handler);
//
// auto handler = boost::bind(&ServerImpl::HandlePipeMessage, this);
// auto listener = boost::bind(&PipeServer::Listen, channel.get(), handler);
//
auto listener = [this](PipeMessage msg, PipeServer::Respond resp) -> void {
HandlePipeMessage(msg, resp);
};
pipeThread = std::make_unique<boost::thread>([this, &listener]() {
channel->Listen(listener);
});
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
int nRet = theLoop.Run();
_Module.RemoveMessageLoop();
return nRet;
}
DWORD ServerImpl::OnEcho(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
if (!m_pRequestHandler)
return 0;
return m_pRequestHandler->FindSession(lParam);
}
DWORD ServerImpl::OnStartSession(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
if (!m_pRequestHandler)
return 0;
return m_pRequestHandler->AddSession(
reinterpret_cast<LPWSTR>(channel->ReceiveBuffer()),
[this](std::wstring &msg) -> bool {
*channel << msg;
return true;
}
);
}
DWORD ServerImpl::OnEndSession(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
if (!m_pRequestHandler)
return 0;
return m_pRequestHandler->RemoveSession(lParam);
}
DWORD ServerImpl::OnKeyEvent(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
if (!m_pRequestHandler/* || !m_pSharedMemory*/)
return 0;
auto eat = [this](std::wstring &msg) -> bool {
*channel << msg;
return true;
};
return m_pRequestHandler->ProcessKeyEvent(KeyEvent(wParam), lParam, eat);
}
DWORD ServerImpl::OnShutdownServer(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
Stop();
return 0;
}
DWORD ServerImpl::OnFocusIn(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
if (!m_pRequestHandler)
return 0;
m_pRequestHandler->FocusIn(wParam, lParam);
return 0;
}
DWORD ServerImpl::OnFocusOut(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
if (!m_pRequestHandler)
return 0;
m_pRequestHandler->FocusOut(wParam, lParam);
return 0;
}
DWORD ServerImpl::OnUpdateInputPosition(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
if (!m_pRequestHandler)
return 0;
/*
* 移位标志 = 1bit == 0
* height: 0~127 = 7bit
* top:-2048~2047 = 12bit(有符号)
* left:-2048~2047 = 12bit(有符号)
*
* 高解析度下:
* 移位标志 = 1bit == 1
* height: 0~254 = 7bit(舍弃低1位)
* top: -4096~4094 = 12bit(有符号,舍弃低1位)
* left: -4096~4094 = 12bit(有符号,舍弃低1位)
*/
RECT rc;
int hi_res = (wParam >> 31) & 0x01;
rc.left = ((wParam & 0x7ff) - (wParam & 0x800)) << hi_res;
rc.top = (((wParam >> 12) & 0x7ff) - ((wParam >> 12) & 0x800)) << hi_res;
const int width = 6;
int height = ((wParam >> 24) & 0x7f) << hi_res;
rc.right = rc.left + width;
rc.bottom = rc.top + height;
if (IsWindows8Point1OrGreater())
{
using PPTLPFPMDPI = BOOL (WINAPI *)(HWND, LPPOINT);
PPTLPFPMDPI PhysicalToLogicalPointForPerMonitorDPI = (PPTLPFPMDPI)::GetProcAddress(m_hUser32Module, "PhysicalToLogicalPointForPerMonitorDPI");
POINT lt = { rc.left, rc.top };
POINT rb = { rc.right, rc.bottom };
PhysicalToLogicalPointForPerMonitorDPI(NULL, <);
PhysicalToLogicalPointForPerMonitorDPI(NULL, &rb);
rc = { lt.x, lt.y, rb.x, rb.y };
}
m_pRequestHandler->UpdateInputPosition(rc, lParam);
return 0;
}
DWORD ServerImpl::OnStartMaintenance(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
if (m_pRequestHandler)
m_pRequestHandler->StartMaintenance();
return 0;
}
DWORD ServerImpl::OnEndMaintenance(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
if (m_pRequestHandler)
m_pRequestHandler->EndMaintenance();
return 0;
}
DWORD ServerImpl::OnCommitComposition(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
if (m_pRequestHandler)
m_pRequestHandler->CommitComposition(lParam);
return 0;
}
DWORD ServerImpl::OnClearComposition(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam)
{
if (m_pRequestHandler)
m_pRequestHandler->ClearComposition(lParam);
return 0;
}
#define MAP_PIPE_MSG_HANDLE(__msg, __wParam, __lParam) {\
auto lParam = __lParam;\
auto wParam = __wParam;\
LRESULT _result = 0;\
switch (__msg) {\
#define PIPE_MSG_HANDLE(__msg, __func) \
case __msg:\
_result = __func(__msg, wParam, lParam);\
break;\
#define END_MAP_PIPE_MSG_HANDLE(__result) }__result = _result; }
template<typename _Resp>
void ServerImpl::HandlePipeMessage(PipeMessage pipe_msg, _Resp resp)
{
DWORD result;
MAP_PIPE_MSG_HANDLE(pipe_msg.Msg, pipe_msg.wParam, pipe_msg.lParam)
PIPE_MSG_HANDLE(WEASEL_IPC_ECHO, OnEcho)
PIPE_MSG_HANDLE(WEASEL_IPC_START_SESSION, OnStartSession)
PIPE_MSG_HANDLE(WEASEL_IPC_END_SESSION, OnEndSession)
PIPE_MSG_HANDLE(WEASEL_IPC_PROCESS_KEY_EVENT, OnKeyEvent)
PIPE_MSG_HANDLE(WEASEL_IPC_SHUTDOWN_SERVER, OnShutdownServer)
PIPE_MSG_HANDLE(WEASEL_IPC_FOCUS_IN, OnFocusIn)
PIPE_MSG_HANDLE(WEASEL_IPC_FOCUS_OUT, OnFocusOut)
PIPE_MSG_HANDLE(WEASEL_IPC_UPDATE_INPUT_POS, OnUpdateInputPosition)
PIPE_MSG_HANDLE(WEASEL_IPC_START_MAINTENANCE, OnStartMaintenance)
PIPE_MSG_HANDLE(WEASEL_IPC_END_MAINTENANCE, OnEndMaintenance)
PIPE_MSG_HANDLE(WEASEL_IPC_COMMIT_COMPOSITION, OnCommitComposition)
PIPE_MSG_HANDLE(WEASEL_IPC_CLEAR_COMPOSITION, OnClearComposition);
PIPE_MSG_HANDLE(WEASEL_IPC_TRAY_COMMAND, OnCommand);
END_MAP_PIPE_MSG_HANDLE(result);
resp(result);
}
PipeServer::PipeServer(std::wstring &&pn_cmd, SECURITY_ATTRIBUTES *s)
: PipeChannel(std::move(pn_cmd), s)
{}
void PipeServer::Listen(ServerHandler const &handler)
{
for (;;) {
HANDLE pipe = INVALID_HANDLE_VALUE;
try {
boost::this_thread::interruption_point();
pipe = _ConnectServerPipe(pname);
boost::thread th([&handler, pipe, this] {
_ProcessPipeThread(pipe, handler);
});
}
catch (DWORD ex) {
_FinalizePipe(pipe);
}
boost::this_thread::interruption_point();
}
}
PipeServer::ServerRunner PipeServer::GetServerRunner(ServerHandler const &handler)
{
return [&handler, this]() {
Listen(handler);
};
}
void PipeServer::_ProcessPipeThread(HANDLE pipe, ServerHandler const &handler)
{
try {
for (;;) {
Res msg;
_Receive(pipe, &msg, sizeof(msg));
handler(msg, [this, pipe](Msg resp) {
_Send(pipe, resp);
});
}
}
catch (...) {
_FinalizePipe(pipe);
}
}
// weasel::Server
Server::Server()
: m_pImpl(new ServerImpl)
{}
Server::~Server()
{
if (m_pImpl)
delete m_pImpl;
}
int Server::Start()
{
return m_pImpl->Start();
}
int Server::Stop()
{
return m_pImpl->Stop();
}
int Server::Run()
{
return m_pImpl->Run();
}
void Server::SetRequestHandler(RequestHandler* pHandler)
{
m_pImpl->SetRequestHandler(pHandler);
}
void Server::AddMenuHandler(UINT uID, CommandHandler handler)
{
m_pImpl->AddMenuHandler(uID, handler);
}
HWND Server::GetHWnd()
{
return m_pImpl->m_hWnd;
}
一、基本概念理解
1.using的具体用法
1、命名空间的使用
一般为了代码的冲突,都会用命名空间。例如,对于Android代码会使用Android作为命名空间。
namespace android;
在code中使用的时候可以用android::加具体的类方法。也可以直接使用using namespace android;
具体的命名空间使用方法不做过多说明。
2、在子类中引用基类的成员
来看下source code:
class T5Base {
public:
T5Base() :value(55) {}
virtual ~T5Base() {}
void test1() { cout << "T5Base test1..." << endl; }
protected:
int value;
};
class T5Derived : private T5Base {
public:
//using T5Base::test1;
//using T5Base::value;
void test2() { cout << "value is " << value << endl; }
};
基类中成员变量value是protected,在private继承之后,对于外界这个值为private,也就是说T5Derived的对象无法使用这个value。
如果想要通过对象使用,需要在public下通过using T5Base::value来引用,这样T5Derived的对象就可以直接使用。
同样的,对于基类中的成员函数test1(),在private继承后变为private,T5Derived的对象同样无法访问,通过using T5Base::test1 就可以使用了。
注意,using只是引用,不参与形参的指定。
3、别名指定
这点就是最开始看到的source code。在C++11中提出了通过using指定别名。
例如上面source code 中:
using value_type = _Ty
以后使用value_type value; 就代表_Ty value;
这个让我们想起了typedef,using 跟typedef有什么区别呢?哪个更好用些呢?
例如:
typedef std::unique_ptr<std::unordered_map<std::string, std::string>> UPtrMapSS;
而C++11中:
using UPtrMapSS = std::unique_ptr<std::unordered_map<std::string, std::string>>;
或许从这个例子中,我们是看不出来明显的好处的(而于我来说,以一个第三者的角度,这个例子也难以说服我一定要用C++11的using)。
再来看下:
typedef void (*FP) (int, const std::string&);
若不是特别熟悉函数指针与typedef的童鞋,我相信第一眼还是很难指出FP其实是一个别名,代表着的是一个函数指针,而指向的这个函数返回类型是void,接受参数是int, const std::string&。那么,让我们换做C++11的写法:
using FP = void (*) (int, const std::string&);
我想,即使第一次读到这样代码,并且知道C++11 using的童鞋也能很容易知道FP是一个别名,using的写法把别名的名字强制分离到了左边,而把别名指向的放在了右边,比较清晰。
而针对这样的例子,我想我可以再补充一个例子:
typedef std::string (Foo::* fooMemFnPtr) (const std::string&);
using fooMemFnPtr = std::string (Foo::*) (const std::string&);
从可读性来看,using也是要好于typedef的。
那么,若是从可读性的理由支持using,力度也是稍微不足的。来看第二个理由,那就是举出了一个typedef做不到,而using可以做到的例子:alias templates, 模板别名。
template <typename T>
using Vec = MyVector<T, MyAlloc<T>>;
// usage
Vec<int> vec;
这一切都会非常的自然。
那么,若你使用typedef来做这一切:
template <typename T>
typedef MyVector<T, MyAlloc<T>> Vec;
// usage
Vec<int> vec;
当你使用编译器编译的时候,将会得到类似:error: a typedef cannot be a template的错误信息。
那么,为什么typedef不可以呢?在 n1449 中提到过这样的话:“we specifically avoid the term “typedef template” and introduce the new syntax involving the pair “using” and “=” to help avoid confusion: we are not defining any types here, we are introducing a synonym (i.e. alias) for an abstraction of a type-id (i.e. type expression) involving template parameters.” 所以,我认为这其实是标准委员会他们的观点与选择,在C++11中,也是完全鼓励用using,而不用typedef的。
具体的可以看下Effective Modern C++
2.std::function函数包装器
std::function简介
std::function是一个函数包装器,该函数包装器模板能包装任何类型的可调用实体,如普通函数,函数对象,lamda表达式等。包装器可拷贝,移动等,并且包装器类型仅仅依赖于调用特征,而不依赖于可调用元素自身的类型。std::function是C++11的新特性,包含在头文件中。
一个std::function类型对象实例可以包装下列这几种可调用实体:函数、函数指针、成员函数、静态函数、lamda表达式和函数对象。std::function对象实例可被拷贝和移动,并且可以使用指定的调用特征来直接调用目标元素。当std::function对象实例未包含任何实际可调用实体时,调用该std::function对象实例将抛出std::bad_function_call异常。
std::function实战
std::function模板类声明
template<class _Rp, class …_ArgTypes>
class _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes…)>
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes…)>,
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes…)>
{ … }
std::function模板类成员函数声明
typedef _Rp result_type;
// construct/copy/destroy:
_LIBCPP_INLINE_VISIBILITY
function() _NOEXCEPT { }
_LIBCPP_INLINE_VISIBILITY
function(nullptr_t) _NOEXCEPT {}
function(const function&);
function(function&&) _NOEXCEPT;
template<class _Fp, class = _EnableIfCallable<_Fp>>
function(_Fp);
#if _LIBCPP_STD_VER <= 14
template
_LIBCPP_INLINE_VISIBILITY
function(allocator_arg_t, const _Alloc&) _NOEXCEPT {}
template
_LIBCPP_INLINE_VISIBILITY
function(allocator_arg_t, const _Alloc&, nullptr_t) _NOEXCEPT {}
template
function(allocator_arg_t, const _Alloc&, const function&);
template
function(allocator_arg_t, const _Alloc&, function&&);
template<class _Fp, class _Alloc, class = _EnableIfCallable<_Fp>>
function(allocator_arg_t, const _Alloc& __a, _Fp __f);
#endif
function& operator=(const function&);
function& operator=(function&&) _NOEXCEPT;
function& operator=(nullptr_t) _NOEXCEPT;
template<class _Fp, class = _EnableIfCallable<_Fp>>
function& operator=(_Fp&&);
~function();
// function modifiers:
void swap(function&) _NOEXCEPT;
#if _LIBCPP_STD_VER <= 14
template<class _Fp, class _Alloc>
_LIBCPP_INLINE_VISIBILITY
void assign(_Fp&& __f, const _Alloc& __a)
{function(allocator_arg, __a, _VSTD::forward<_Fp>(__f)).swap(*this);}
#endif
// function capacity:
_LIBCPP_INLINE_VISIBILITY
_LIBCPP_EXPLICIT operator bool() const _NOEXCEPT {
return static_cast<bool>(__f_);
}
// deleted overloads close possible hole in the type system
template<class _R2, class... _ArgTypes2>
bool operator==(const function<_R2(_ArgTypes2...)>&) const = delete;
template<class _R2, class... _ArgTypes2>
bool operator!=(const function<_R2(_ArgTypes2...)>&) const = delete;
public:
// function invocation:
_Rp operator()(_ArgTypes…) const;
#ifndef _LIBCPP_NO_RTTI
// function target access:
const std::type_info& target_type() const _NOEXCEPT;
template _Tp* target() _NOEXCEPT;
template const _Tp* target() const _NOEXCEPT;
#endif // _LIBCPP_NO_RTTI
从成员函数里我们知道std::function对象实例不允许进行==和!=比较操作,std::function模板类实例最终调用成员函数_Rp operator()(_ArgTypes…) const进而调用包装的调用实体。1、std::function包装函数指针
定义一个std::function<int(int)>对象实例
std::function<int(int)> callback;
std::function对象实例包装函数指针
int (*fun_ptr)(int);
int fun1(int a){
return a;
}
int main(int argc, char *argv[]){
std::cout << “Hello world” << std::endl;
fun_ptr = fun1; //函数指针fun_ptr指向fun1函数
callback = fun_ptr; //std::function对象包装函数指针
std::cout << callback(10) << std::endl; //std::function对象实例调用包装的实体
return 0;
}
2、std::function包装函数
int fun1(int a){
return a;
}
int main(int argc, char *argv[]){
std::cout << “Hello world” << std::endl;
callback = fun1; //std::function包装函数
std::cout << callback(42) << std::endl; //std::function对象实例调用包装的调用实体
return 0;
}
3、std::function包装模板函数
template
T fun2(T a){
return a + 2;
}
int main(int argc, char *argv[]){
std::cout << “Hello world” << std::endl;
callback = fun2<int>; //std::function包装模板函数
std::cout << callback(10) << std::endl; //std::function对象实例调用包装的调用实体
return 0;
}
4、std::function包装函数对象
struct add{
int operator()(int x){
return x + 9;
}
};
int main(int argc, char *argv[]){
std::cout << “Hello world” << std::endl;
callback = add(); //std::function包装对象函数
std::cout << callback(2) << std::endl; //std::function对象实例调用包装的调用实体
return 0;
}
5、std::function包装lamda表达式
int main(int argc, char *argv[]){
std::cout << “Hello world” << std::endl;
auto fun3 = [](int a) {return a * 2;}; //lamda表达式
callback = fun3; //std::function包装lamda表达式
std::cout << callback(9) << std::endl; //std::function对象实例调用包装的调用实体
return 0;
}
6、std::function包装模板对象函数
template
struct sub{
T operator()(T a){
return a - 8;
}
};
int main(int argc, char *argv[]){
std::cout << “Hello world” << std::endl;
callback = sub<int>(); //std::function包装模板对象函数
std::cout << callback(2) << std::endl; //std::function对象实例调用包装的调用实体
return 0;
}
7、std::function包装模板对象静态函数
template
struct foo2{
static T foo(T a){
return a * 4;
}
};
int main(int argc, char *argv[]){
std::cout << “Hello world” << std::endl;
callback = foo2<int>::foo; //std::function包装模板对象静态函数
std::cout << callback(3) << std::endl; //std::function对象实例调用包装的调用实体
return 0;
}
8、std::function包装对象静态函数
struct foo1{
static int foo(int a){
return a * 3;
}
};
int main(int argc, char *argv[]){
std::cout << “Hello world” << std::endl;
callback = foo1::foo; //std::function包装对象静态函数
std::cout << callback(5) << std::endl; //std::function对象实例调用包装的调用实体
return 0;
}
9、std::function包装类成员函数
struct foo3{
int foo(int a){
return a * a;
}
};
int main(int argc, char *argv[]){
std::cout << “Hello world” << std::endl;
foo3 test_foo1;
callback = std::bind(&foo3::foo, test_foo1, std::placeholders::_1); //std::function包装类成员函数
std::cout << callback(9) << std::endl; //std::function对象实例调用包装的调用实体
return 0;
}
这里我们用到了std::bind,C++11中std::bind函数的意义就如字面上的意思一样,用来绑定函数调用的某些参数。std::bind的思想其实是一种延迟计算的思想,将可调用对象保存起来,然后在需要的时候再调用。而且这种绑定是非常灵活的,不论是普通函数还是函数对象还是成员函数都可以绑定,而且其参数可以支持占位符。
这里的std::placeholders::_1是一个占位符,且绑定第一个参数,若可调用实体有2个形参,那么绑定第二个参数的占位符是std::placeholders::_2。
10、std::function包装模板类成员函数
template
struct foo4{
T foo(T a){
return a * 6;
}
};
int main(int argc, char *argv[]){
std::cout << “Hello world” << std::endl;
foo4<int> test_foo2;
callback = std::bind(&foo4<int>::foo, test_foo2, std::placeholders::_1); //std::function包装模板类成员函数
std::cout << callback(7) << std::endl; //std::function对象实例调用包装的调用实体
return 0;
}
11、std::function拷贝、移动
int main(int argc, char *argv[]){
std::cout << “Hello world” << std::endl;
std::function<int(int)> callback2 = callback; //拷贝赋值运算符
std::cout << callback2(7) << std::endl;
std::function<int(int)>&& callback3 = std::move(callback); //移动赋值运算符
std::cout << callback3(7) << std::endl;
std::cout << callback(7) << std::endl;
std::function<int(int)> callback4(callback); //拷贝
std::cout << callback4(7) << std::endl;
return 0;
}