使用 lambdas/bind 的成员函数实现中间件函数
Implementing middleware function using member functions with lambdas/bind
我有一个函数 Post()
,它有两个参数 - std::string
用于侦听请求的路径,以及 std::function<void(Request &, Response &)>
用于处理传入请求的路径。请注意,我无法修改 Post()
.
例如:
m_server.Post("/wallet/open", [this](auto req, auto res)
{
std::cout << req.body << std::endl;
}
我正在尝试通过中间件函数传递请求,然后转发到处理程序函数。
处理函数和中间件函数是成员函数。 Post() 绑定的设置发生在同一个 class.
的成员函数中
这个有效:
m_server.Post("/wallet/open", [this](auto req, auto res){
auto f = std::bind(&ApiDispatcher::openWallet, this, _1, _2);
middleware(req, res, f);
});
但是,我正在连接合理数量的请求,最好将其抽象为一个函数,这样我们就可以调用,例如
m_server.Post("/wallet/open", router(openWallet));
我设法让类似的东西工作,但我不知道如何在使用成员函数时让它工作。如果一切都是免费功能,这会很好用:
const auto router = [](auto function)
{
return std::bind(middleware, _1, _2, function);
};
m_server.Post("/wallet/open", router(openWallet))
.Post("/wallet/keyimport", router(keyImportWallet))
.Post("/wallet/seedimport", router(seedImportWallet))
.Post("/wallet/viewkeyimport", router(importViewWallet))
.Post("/wallet/create", router(createWallet))
但是,我试图让它为成员函数工作的尝试不起作用,而且我无法真正弄清楚错误消息告诉我的是什么:
const auto router = [](auto function)
{
return std::bind(&ApiDispatcher::middleware, _1, _2, function);
};
m_server.Post("/wallet/open", router(&ApiDispatcher::openWallet))
.Post("/wallet/keyimport", router(&ApiDispatcher::keyImportWallet))
.Post("/wallet/seedimport", router(&ApiDispatcher::seedImportWallet))
.Post("/wallet/viewkeyimport", router(&ApiDispatcher::importViewWallet))
.Post("/wallet/create", router(&ApiDispatcher::createWallet))
路由器功能本身就可以工作。我认为问题是我没有在 this
上调用处理函数。我尝试这样做:
m_server.Post("/wallet/open", router(std::bind(&ApiDispatcher::openWallet, this, _1, _2)));
但后来我收到关于 "Wrong number of arguments for pointer-to-member" 的错误。
这里有一个最简单的例子来重现这个问题:
#include <iostream>
#include <string>
#include <functional>
using namespace std::placeholders;
class ApiDispatcher
{
public:
void middleware(std::string req, std::string res, std::function<void(std::string req, std::string res)> handler)
{
// do some processing
handler(req + " - from the middleware", res);
}
void dispatch()
{
const auto router = [](auto function)
{
return std::bind(&ApiDispatcher::middleware, _1, _2, function);
};
/* Uncommenting this line breaks compilation */
// Post("/wallet/open", router(&ApiDispatcher::openWallet));
}
void openWallet(std::string req, std::string res)
{
std::cout << req << std::endl;
}
void Post(std::string method, std::function<void(std::string req, std::string res)> f)
{
// wait for a http request
f("request", "response");
}
};
int main()
{
ApiDispatcher server;
server.dispatch();
}
谢谢。抱歉 post 太长了。
您的路由器函数需要 return 一个函数,该函数将使用 class 的特定实例调用指定的成员函数。
void dispatch()
{
const auto router = [this](auto function) {
return std::bind(function, this, _1, _2);
};
Post("/wallet/open", router(&ApiDispatcher::openWallet));
}
或者,我最初错过了这个,通过中间件函数路由所有内容,我能找到的最简单的方法是。
void dispatch()
{
const auto router = [this](auto function) {
return [this, function](auto a, auto b) {
middleware(a, b, std::bind(function, this, _1, _2));
};
};
Post("/wallet/open", router(&ApiDispatcher::openWallet));
}
或者,如果您更愿意使用绑定,那么您必须首先强制将内部绑定设为函数,然后执行。
void dispatch()
{
const auto router = [this](auto function) {
std::function<void(std::string, std::string)> func = std::bind(function, this, _1, _2);
return std::bind(&ApiDispatcher::middleware, this, _1, _2, func);
};
Post("/wallet/open", router(&ApiDispatcher::openWallet));
}
我发现使用 lambda 更容易推理,据我所知,出于性能原因,它们比 std::bind
更受青睐。
void dispatch()
{
Post("/wallet/open", [&](std::string req_1, std::string res_1){
middleware(req_1, res_1, [&](std::string req_2, std::string res_2){
openWallet(req_2, res_2);
});
});
}
对成员函数的每次调用都包装在此处的 lambda 中。
您可能应该使用 const std::string&
作为参数以避免所有调用的副本。
还有一点要指出性能。只要您只是转发函数调用(您没有存储可调用对象供以后使用),您就可以将 middleware
和 Post
更改为模板。可调用项将是模板参数,您不必支付 std::function
.
的开销
template <typename F>
void middleware(std::string req, std::string res, F handler)
{
// do some processing
handler(req + " - from the middleware", res);
}
template <typename F>
void Post(std::string method, F f)
{
// wait for a http request
f("request", "response");
}
编辑
要按照我认为的方式提取一些逻辑,您需要传递一个指向成员函数的指针。
void dispatch()
{
auto const router = [&](auto m_fptr) {
return [&, m_fptr](std::string req, std::string res) {
middleware(req, res, [&](std::string req_2, std::string res_2) {
(this->*m_fptr)(req_2, res_2);
});
};
};
Post("/wallet/open", router(&ApiDispatcher::openWallet));
}
不确定...但在我看来你的想法是写这个或类似的东西
const auto router = [this](auto function)
{
std::function<void(std::string req, std::string res)> fn
= std::bind(function, this, _1, _2);
return std::bind(&ApiDispatcher::middleware, this, _1, _2, fn);
};
但我建议遵循super提出的lambda-inside-lambda方案
问题的根源在于 router
lambda 上的参数以及 lambda 内部的 std::bind
将如何解释它。
假设您已在 dispatch
内修复对 router
的调用,替换为:
Post("/wallet/open", router(&ApiDispatcher::openWallet));
通过适当的形式:
Post("/wallet/open", router(std::bind(&ApiDispatcher::openWallet, this, _1, _2)));
然后 cppreference,lambda router
的 function
参数将被推断为绑定表达式,因此 [=16= 中的 std::bind
] 将看到此参数满足 std::is_bind_expression<T>::value == true
并且在调用 middleware
.
时不会尝试转换为 std::function
要修复它,您需要在 router
lambda 上明确指定 function
参数的类型,如:
const auto router = [this](std::function<void(std::string req, std::string res)> function)
{
return std::bind(&ApiDispatcher::middleware, this, _1, _2, function);
};
这样做是对
的隐式转换
std::bind(&ApiDispatcher::openWallet, this, _1, _2)
到std::function
发生在调用
之前
std::bind(&ApiDispatcher::middleware, this, _1, _2, function)
或者您保留 auto
,并在 lambda 中触发隐式转换:
const auto router = [this](auto function)
{
std::function<void(std::string req, std::string res)> func = function;
return std::bind(&ApiDispatcher::middleware, this, _1, _2, func);
};
我有一个函数 Post()
,它有两个参数 - std::string
用于侦听请求的路径,以及 std::function<void(Request &, Response &)>
用于处理传入请求的路径。请注意,我无法修改 Post()
.
例如:
m_server.Post("/wallet/open", [this](auto req, auto res)
{
std::cout << req.body << std::endl;
}
我正在尝试通过中间件函数传递请求,然后转发到处理程序函数。
处理函数和中间件函数是成员函数。 Post() 绑定的设置发生在同一个 class.
的成员函数中这个有效:
m_server.Post("/wallet/open", [this](auto req, auto res){
auto f = std::bind(&ApiDispatcher::openWallet, this, _1, _2);
middleware(req, res, f);
});
但是,我正在连接合理数量的请求,最好将其抽象为一个函数,这样我们就可以调用,例如
m_server.Post("/wallet/open", router(openWallet));
我设法让类似的东西工作,但我不知道如何在使用成员函数时让它工作。如果一切都是免费功能,这会很好用:
const auto router = [](auto function)
{
return std::bind(middleware, _1, _2, function);
};
m_server.Post("/wallet/open", router(openWallet))
.Post("/wallet/keyimport", router(keyImportWallet))
.Post("/wallet/seedimport", router(seedImportWallet))
.Post("/wallet/viewkeyimport", router(importViewWallet))
.Post("/wallet/create", router(createWallet))
但是,我试图让它为成员函数工作的尝试不起作用,而且我无法真正弄清楚错误消息告诉我的是什么:
const auto router = [](auto function)
{
return std::bind(&ApiDispatcher::middleware, _1, _2, function);
};
m_server.Post("/wallet/open", router(&ApiDispatcher::openWallet))
.Post("/wallet/keyimport", router(&ApiDispatcher::keyImportWallet))
.Post("/wallet/seedimport", router(&ApiDispatcher::seedImportWallet))
.Post("/wallet/viewkeyimport", router(&ApiDispatcher::importViewWallet))
.Post("/wallet/create", router(&ApiDispatcher::createWallet))
路由器功能本身就可以工作。我认为问题是我没有在 this
上调用处理函数。我尝试这样做:
m_server.Post("/wallet/open", router(std::bind(&ApiDispatcher::openWallet, this, _1, _2)));
但后来我收到关于 "Wrong number of arguments for pointer-to-member" 的错误。
这里有一个最简单的例子来重现这个问题:
#include <iostream>
#include <string>
#include <functional>
using namespace std::placeholders;
class ApiDispatcher
{
public:
void middleware(std::string req, std::string res, std::function<void(std::string req, std::string res)> handler)
{
// do some processing
handler(req + " - from the middleware", res);
}
void dispatch()
{
const auto router = [](auto function)
{
return std::bind(&ApiDispatcher::middleware, _1, _2, function);
};
/* Uncommenting this line breaks compilation */
// Post("/wallet/open", router(&ApiDispatcher::openWallet));
}
void openWallet(std::string req, std::string res)
{
std::cout << req << std::endl;
}
void Post(std::string method, std::function<void(std::string req, std::string res)> f)
{
// wait for a http request
f("request", "response");
}
};
int main()
{
ApiDispatcher server;
server.dispatch();
}
谢谢。抱歉 post 太长了。
您的路由器函数需要 return 一个函数,该函数将使用 class 的特定实例调用指定的成员函数。
void dispatch()
{
const auto router = [this](auto function) {
return std::bind(function, this, _1, _2);
};
Post("/wallet/open", router(&ApiDispatcher::openWallet));
}
或者,我最初错过了这个,通过中间件函数路由所有内容,我能找到的最简单的方法是。
void dispatch()
{
const auto router = [this](auto function) {
return [this, function](auto a, auto b) {
middleware(a, b, std::bind(function, this, _1, _2));
};
};
Post("/wallet/open", router(&ApiDispatcher::openWallet));
}
或者,如果您更愿意使用绑定,那么您必须首先强制将内部绑定设为函数,然后执行。
void dispatch()
{
const auto router = [this](auto function) {
std::function<void(std::string, std::string)> func = std::bind(function, this, _1, _2);
return std::bind(&ApiDispatcher::middleware, this, _1, _2, func);
};
Post("/wallet/open", router(&ApiDispatcher::openWallet));
}
我发现使用 lambda 更容易推理,据我所知,出于性能原因,它们比 std::bind
更受青睐。
void dispatch()
{
Post("/wallet/open", [&](std::string req_1, std::string res_1){
middleware(req_1, res_1, [&](std::string req_2, std::string res_2){
openWallet(req_2, res_2);
});
});
}
对成员函数的每次调用都包装在此处的 lambda 中。
您可能应该使用 const std::string&
作为参数以避免所有调用的副本。
还有一点要指出性能。只要您只是转发函数调用(您没有存储可调用对象供以后使用),您就可以将 middleware
和 Post
更改为模板。可调用项将是模板参数,您不必支付 std::function
.
template <typename F>
void middleware(std::string req, std::string res, F handler)
{
// do some processing
handler(req + " - from the middleware", res);
}
template <typename F>
void Post(std::string method, F f)
{
// wait for a http request
f("request", "response");
}
编辑
要按照我认为的方式提取一些逻辑,您需要传递一个指向成员函数的指针。
void dispatch()
{
auto const router = [&](auto m_fptr) {
return [&, m_fptr](std::string req, std::string res) {
middleware(req, res, [&](std::string req_2, std::string res_2) {
(this->*m_fptr)(req_2, res_2);
});
};
};
Post("/wallet/open", router(&ApiDispatcher::openWallet));
}
不确定...但在我看来你的想法是写这个或类似的东西
const auto router = [this](auto function)
{
std::function<void(std::string req, std::string res)> fn
= std::bind(function, this, _1, _2);
return std::bind(&ApiDispatcher::middleware, this, _1, _2, fn);
};
但我建议遵循super提出的lambda-inside-lambda方案
问题的根源在于 router
lambda 上的参数以及 lambda 内部的 std::bind
将如何解释它。
假设您已在 dispatch
内修复对 router
的调用,替换为:
Post("/wallet/open", router(&ApiDispatcher::openWallet));
通过适当的形式:
Post("/wallet/open", router(std::bind(&ApiDispatcher::openWallet, this, _1, _2)));
然后 cppreference,lambda router
的 function
参数将被推断为绑定表达式,因此 [=16= 中的 std::bind
] 将看到此参数满足 std::is_bind_expression<T>::value == true
并且在调用 middleware
.
std::function
要修复它,您需要在 router
lambda 上明确指定 function
参数的类型,如:
const auto router = [this](std::function<void(std::string req, std::string res)> function)
{
return std::bind(&ApiDispatcher::middleware, this, _1, _2, function);
};
这样做是对
的隐式转换std::bind(&ApiDispatcher::openWallet, this, _1, _2)
到std::function
发生在调用
std::bind(&ApiDispatcher::middleware, this, _1, _2, function)
或者您保留 auto
,并在 lambda 中触发隐式转换:
const auto router = [this](auto function)
{
std::function<void(std::string req, std::string res)> func = function;
return std::bind(&ApiDispatcher::middleware, this, _1, _2, func);
};