4

我有一个函数,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() 绑定的设置发生在同一类的成员函数中。

这有效:

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)));

但后来我收到一个关于“指向成员的参数数量错误”的错误。

这是一个复制问题的最小示例:

#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();
}

谢谢。抱歉帖子太长了。

4

4 回答 4

2

您的路由器函数需要返回一个函数,该函数将使用该类的特定实例调用指定的成员函数。

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));
}
于 2018-11-21T22:14:17.140 回答
2

我发现使用 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&用作参数以避免所有调用的副本。

另一个需要指出的性能。只要您只是转发函数调用(您不存储可调用以供以后使用),您就可以更改middlewarePost模板。可调用对象将是模板参数,您无需支付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));
}
于 2018-11-21T22:21:20.967 回答
1

不确定......但在我看来你的想法是写这个或类似的东西

    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 解决方案。

于 2018-11-21T22:30:26.480 回答
1

问题的根源在于 lambda 上的参数以及routerlambdastd::bind内部将如何解释它。

假设您已经修复了对routerinside的调用dispatch,替换:

Post("/wallet/open", router(&ApiDispatcher::openWallet));

通过适当的形式:

Post("/wallet/open", router(std::bind(&ApiDispatcher::openWallet, this, _1, _2)));

然后通过cppreferencefunctionlambda 的参数router将被推断为绑定表达式,因此std::bind内部router将看到此参数满足并且在调用时不会尝试std::is_bind_expression<T>::value == true转换。std::functionmiddleware

要修复它,您需要明确指定lambdafunction上的参数类型,如:router

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)

tostd::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);
};
于 2018-11-21T22:30:44.887 回答