2

我正在尝试从是否可以编写模板来检查函数的存在?并应用它们,但我遇到了一些麻烦。我定义了以下内容:

#define HAS_MEM_FUNC(func, name)                                        \
    template<typename T, typename Sign>                                 \
    struct name {\
        typedef char yes[1];                                            \
        typedef char no[2];                                            \
        template <typename U, U> struct type_check;                     \
        template <typename _1> static yes &chk(type_check<Sign, &_1::func> *); \
        template <typename   > static no  &chk(...);                    \
        static bool const value = sizeof(chk<T>(0)) == sizeof(yes);     \
    }

// Creates a member function detector and two overloads of a message handler
// registration function, one that registers a member function of a specific name
// if it exists and another that does nothing if that member function doesn't exist.

#define DEFINE_REGISTER_FUNC(msgid, msgtype, handlername) \
    HAS_MEM_FUNC(handlername, has_##handlername);\
    template <typename T>\
    static void register_##handlername(\
        T* pWnd, \
        typename std::enable_if<has_##handlername<T, void(T::*)(msgtype&)>::value, T>::type* t = nullptr) \
    {\
        (void) t;\
        pWnd->setMessageHandler(\
            msgid,\
            Handler<msgtype>(std::bind(&T::handlername, pWnd, std::placeholders::_1)));\
    }\
    \
    template <typename T> \
    static void register_##handlername(\
        T* pWnd, \
        typename std::enable_if<!has_##handlername<T, void(T::*)(msgtype&)>::value, T>::type* t = nullptr) \
    {\
        (void)pWnd;\
        (void)t;\
    }

我也有一个这样定义的类:

template <typename T>
class RegisterHandlers
{
public:
    template <typename T>
    static void registerHandlers(T* pWnd)
    {
        register_onCreate(pWnd);
        register_onPaint(pWnd);
    }

private:
    DEFINE_REGISTER_FUNC(WM_CREATE, CreateMessage, onCreate);
    DEFINE_REGISTER_FUNC(WM_PAINT, PaintMessage, onPaint);
};

这个想法是,如果类T有一个成员函数onCreate(CreateMessage&),它会自动注册为处理程序WM_CREATE。如果它没有这样的方法,那么register_onCreate将调用 do-nothing 重载,编译器会很高兴。onPaint 处理程序也是如此(我将其包括在内主要是为了说明 SFINAE确实有效)。

如果我有这样的课程,这很好用:

class MainWnd
{
public:
    friend class RegisterHandlers<MainWnd>;

    MainWnd()
    {
        RegisterHandlers<MainWnd>::registerHandlers(this);
    }

protected:
    void onCreate(CreateMessage&) { /* do some stuff */ }
};

但是,在我添加此内容的那一刻:

class SubWnd : public MainWnd
{
public:
    friend class RegisterHandlers<SubWnd>;

    SubWnd()
    {
        RegisterHandlers<SubWnd>::registerHandlers(this);
    }

protected:
    void onPaint(PaintMessage&) { /* do some stuff */ }
};

我收到一个无法访问的错误MainWnd::onCreate(无法访问受保护的成员)。我分解了宏以帮助找到问题实际发生的位置,它似乎在HAS_MEM_FUNC宏中,可能在这一行:

template <typename _1> static yes &chk(type_check<Sign, &_1::func> *);

所以,我真的有两个问题:

  1. 是否可以在不公开事件处理程序的情况下做我想做的事情?
  2. 如果基类已经这样做了,是否也可以避免重新注册事件处理程序?换一种说法,有没有办法判断函数是在基类中声明的,还是源自派生类?

如果它是相关的,我正在尝试在 Visual Studio 2013 Preview 编译器上执行此操作。这个问题的动机是我正在试验 C++11 功能和 Windows API 包装器,我试图看看是否有比大型 vtable 和消息映射宏更好的解决方案来将消息路由到适当的处理程序。

4

1 回答 1

1

这里的障碍是:

SubWnd派生自的类MinWnd声明友谊:

friend class RegisterHandlers<SubWnd>;

这样它就RegisterHandlers<SubWnd>::registerHandlers(this)可以注册它自己的受保护处理程序onPaint,也可以冗余地注册onCreate它继承自的受保护处理程序MainWnd。后一种注册是多余的,因为基类MainWnd的构造函数已经注册onCreateRegisterHandlers<T>::registerHandlers(WinType *) 总是要“注册”任何窗口可能具有的所有处理程序,如果WinType 实际上没有给定的处理程序,则执行无操作,如果已由基类构造函数完成,则重复注册。

但是要注册onCreate,无论是否冗余,它是 的受保护成员MainWndRegisterHandlers<SubWnd>需要成为 的朋友MainWnd。它不是:它只是 的朋友SubWnd

您无法摆脱RegisterHandlers<T> 按窗口类型的模板参数化,T因为您依赖于 SFINAE 方法内省的模板参数

但我不得不说,这种 SFINAE 内省方法在本案中是徒劳的。您的每个窗口类构造函数仍然需要做一些事情来注册自己的处理程序。您希望它执行此操作的方式是将任务委派给RegisterHandlers<T>::registerHandlers(WinType *),这将:

  • 尝试注册任何窗口可能拥有的所有处理程序,
  • WinType没有处理程序时什么都不做,
  • WinType当' 的基类构造函数已经注册它时,冗余地重新注册处理程序,并且
  • 非冗余且成功地注册WinType.

每种窗口类型实际上但不可避免地需要做的所有这一切的唯一部分是最后一个。所以对于你的两个问题:

  1. 是否可以在不公开事件处理程序的情况下做我想做的事情?

是的。只需让每个窗口类型注册自己的处理程序。

  1. 如果基类已经这样做了,是否也可以避免重新注册事件处理程序?

是的。一样的答案。

我很欣赏你只是在做实验,但这个实验并不乐观。

您在问题 2 中附加了第三个问题:

有没有办法判断函数是在基类中声明的还是源自派生类

是的。如果您再次查阅此答案,然后还查阅我对下面同一问题的答案 ,您将看到第一个解决方案检测到成员函数T::mf whenmf定义于T而不是 whenmf被继承T,而我的解决方案mf在任何一种情况下都检测到。因此,您可以通过这两种方法进行 SFINAE 探测,并且如果第一种方法没有检测到它并且第二种方法检测到它,那么您T::mf就会知道它是继承的。mf

于 2013-07-20T10:48:11.620 回答