【问题标题】:Function pointer to member function T(U) where T and U may or may not be void指向成员函数 T(U) 的函数指针,其中 T 和 U 可能为空,也可能不为空
【发布时间】:2022-08-22 21:06:04
【问题描述】:

我有一个类Frobnicator 处理各种请求。

class Frobnicator
{
    public:

        // Handlers are member functions.
        // They optionally take some input. They optionally return some output. But they always take the context!
        // There are more types than just int involved, but it\'s always just one input or void, and one output or void.
        void performSomething(Context* context)            { /* ... */ } // Takes void, returns void
        void setSomething    (Context* context, int input) { /* ... */ } // Takes int , returns void
        int  getSomething    (Context* context)            { /* ... */ } // Takes void, returns int
        int  convertSomething(Context* context, int input) { /* ... */ } // Takes int , returns int

        template<typename TResult, typename TParameter>
        void registerHandler(std::string identifier, TResult(Frobnicator::* handler)(Context*, TParameter))
        {
            // The external API actually wants a callback that takes and returns JSON. We give it a lambda that does the conversion and calls the actual member function.
            // The identifier tells the external API which callback to call for which request. It\'s not relevant for this question, just to show the idea. Think of something like a REST API.
            someExternalApiThatWantsJson.registerHandler(identifier, [&](Context* context, Json input)
            {
                // Idealy, this would be a one-liner.
                //return Json::convertFrom((this->*handler)(context, input.convertTo<TParameter>()));
                // But calling Json.convertTo<void>() and Json::convertFrom(void) does not work automagically anyways, so we need to split it up manually:
                Json result;
                if constexpr (std::is_same<TResult, void>::value)
                    if constexpr (std::is_same<TParameter, void>::value)                            (this->*handler)(context                               ) ; // Takes void, returns void
                    else                                                                            (this->*handler)(context, input.convertTo<TParameter>()) ; // Takes something, returns void
                else
                    if constexpr (std::is_same<TParameter, void>::value) result = Json::convertFrom((this->*handler)(context                               )); // Takes void, returns something
                    else                                                 result = Json::convertFrom((this->*handler)(context, input.convertTo<TParameter>())); // Takes something, returns something
                return result;
            });
        }

        // Set up the handlers.
        void setup()
        {
            // The problem is that some of these calls don\'t work:
            registerHandler            (\"PerformSomething\", &Frobnicator::performSomething); // \"failed template argument deduction\"
            registerHandler<void, void>(\"PerformSomething\", &Frobnicator::performSomething); // Trying to specify the types explicitly: \"substitution failure [with TResult = void, TParameter = void]: argument may not have \'void\' type\"
            registerHandler            (\"SetSomething\"    , &Frobnicator::setSomething);     // Compiles fine
            registerHandler            (\"GetSomething\"    , &Frobnicator::getSomething);     // \"failed template argument deduction\"
            registerHandler<int , void>(\"GetSomething\"    , &Frobnicator::getSomething);     // Trying to specify the types explicitly: \"substitution failure [with TResult = int, TParameter = void]: argument may not have \'void\' type\"
            registerHandler            (\"ConvertSomething\", &Frobnicator::convertSomething); // Compiles fine
        }

};

TResult 可以是 intvoid,它工作正常。但它仅在TParameter 不是void 时有效。
我怎样才能让registerHandler 也接受指向不带参数的函数的指针?

这个想法是让成员函数的签名非常干净,并且对registerHandler 的调用大多是干净的。所以给performSomethinggetSomething 一个虚拟参数是不可能的。在调用registerHandler 时手动指定类型很难看,但如果有必要,我会接受。

registerHandler 的主体相对较短,主要用于区分 void 和 non-void,因此在 TParametervoid 时提供专门化将是一个很好的解决方案:

template<typename TResult>
void registerHandler<TResult, void>(std::string identifier, TResult(Frobnicator::* handler)(Context*))

除了“不允许函数模板偏特化”。

    标签: c++ c++17


    【解决方案1】:

    出色地。有时你浪费了一个小时,因为你差点错过了一个解决方案。只是不要将其作为规范:

    template<typename TResult>
    void registerHandler(std::string identifier, TResult(Frobnicator::* handler)(Context*))
    

    但是!:这对我来说是一个有效的解决方案,但是如果有一个不需要复制几乎整个函数体的解决方案,那就太好了。因此,绝对欢迎更好的答案!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-12
      相关资源
      最近更新 更多