简体   繁体   English

我可以检测成员函数是否存在,并且朋友可以用C ++访问吗?

[英]Can I detect if a member function exists and is accessible to a friend in C++?

I am trying to take the ideas from Is it possible to write a template to check for a function's existence? 我试图从中汲取灵感。 是否可以编写模板来检查函数的存在? and apply them, but I am running into some trouble. 并应用它们,但是我遇到了一些麻烦。 I have the following defined: 我有以下定义:

#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;\
    }

I also have a class defined like this: 我也有一个这样定义的类:

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

The idea is that if the class T has a member function onCreate(CreateMessage&) , it will automatically be registered as the handler for WM_CREATE . 这个想法是,如果类T具有成员函数onCreate(CreateMessage&) ,它将自动注册为WM_CREATE的处理程序。 If it has no such method, then the do-nothing overload of register_onCreate will be called and the compiler will be happy. 如果没有这种方法,则将调用register_onCreate空操作重载,并且编译器将满意。 Same for the onPaint handler (I included that mostly to illustrate that the SFINAE does work). 对于onPaint处理程序也是如此(我主要是为了说明SFINAE 确实起作用)。

This works fine if I have a class like this: 如果我有一个像这样的课,这很好用:

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

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

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

However, the moment I add this: 但是,现在我添加以下内容:

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

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

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

I get an error that MainWnd::onCreate is inaccessible (cannot access protected member). 我收到MainWnd::onCreate无法访问的错误(无法访问受保护的成员)。 I broke down the macros to help find where the problem is actually occurring, and it seems to be in the HAS_MEM_FUNC macro, probably at this line: 我分解了宏以帮助查找实际发生问题的位置,它似乎在HAS_MEM_FUNC宏中,可能在此行:

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

So, I have two questions really: 因此,我确实有两个问题:

  1. Is it possible to do what I am trying to do without making the event handlers public? 是否可以在不公开事件处理程序的情况下做我想做的事情?
  2. Is it also possible to avoid re-registering event handlers if the base class already did it? 如果基类已经做过,是否还可以避免重新注册事件处理程序? Phrased another way, is there a way to tell if the function was declared in the base class or if it originated in the derived class? 换一种说法,有没有办法判断该函数是在基类中声明的,还是起源于派生类中的?

In case it's relevant, I am trying to do this on the Visual Studio 2013 Preview compiler. 如果相关,我尝试在Visual Studio 2013预览编译器上进行。 The motivation for this question is that I am experimenting with C++11 features and Windows API wrappers, and I am trying to see if there are better solutions than large vtables and message map macros for routing messages to an appropriate handler. 这个问题的动机是我正在试验C ++ 11功能和Windows API包装器,并且试图查看是否有比大型vtables和消息映射宏更好的解决方案,用于将消息路由到适当的处理程序。

The obstacle here is this: 这里的障碍是:

Class SubWnd , derived from MinWnd , declares friendship: SubWnd ,源自MinWnd ,宣布友谊:

friend class RegisterHandlers<SubWnd>;

so that RegisterHandlers<SubWnd>::registerHandlers(this) can do the registering of its own protected handler onPaint and also - redundantly - of the protected handler onCreate that it inherits from MainWnd . 因此RegisterHandlers<SubWnd>::registerHandlers(this)可以注册自己的受保护处理程序onPaint ,也可以冗余地注册它从MainWnd继承的受保护处理程序onCreate The latter registration is redundant because the base class MainWnd 's constructor will already have registered onCreate . 后者的注册是多余的,因为基类MainWnd的构造函数将已经在onCreate注册。 RegisterHandlers<T>::registerHandlers(WinType *) is always going to "register" all the handlers that any window might have, doing a no-op if WinType doesn't actually have a given handler and repeating the registration if it has already been done by a base class constructor. RegisterHandlers<T>::registerHandlers(WinType *)始终会“注册”任何窗口可能具有的所有处理程序,如果WinType实际上没有给定的处理程序, WinType执行操作,如果已经存在,则重复注册由基类构造函数完成。

But to register onCreate , redundantly or not, which is a protected member of MainWnd , RegisterHandlers<SubWnd> needs to be a friend of MainWnd . 但是要注册是否为MainWnd的受保护成员的onCreate (是否冗余), RegisterHandlers<SubWnd>必须成为MainWnd的朋友。 And it isn't: it is only a friend of SubWnd . 它不是:它只是SubWnd的朋友。

You cannnot get rid of the template parameterization of RegisterHandlers<T> by window-type T because you depend on that template parameter for your SFINAE method-introspecting 您无法通过窗口类型T摆脱RegisterHandlers<T>的模板参数化,因为您的SFINAE方法自检依赖于该模板参数

But this SFINAE method-introspecting, I have to say, is a futile palaver in the present case. 但是,我不得不说,这种SFINAE自省方法在目前情况下是徒劳的。 Every one of your window-class constructors still has to do something to get its own handlers registered. 您的每个窗口类构造函数都必须做一些事情才能注册自己的处理程序。 The way that you want it to go about this is by delegating the task to RegisterHandlers<T>::registerHandlers(WinType *) , which will: 您希望它执行此操作的方法是将任务委托给RegisterHandlers<T>::registerHandlers(WinType *) ,它将:

  • Attempt to register all handlers any window could have, 尝试注册任何窗口可能具有的所有处理程序,
  • Doing nothing whenever WinType doesn't have the handler, 只要WinType没有处理程序,什么WinType做,
  • Redundantly re-registering the handler when WinType 's base class constructor has already registered it, and WinType的基类构造函数已经注册处理程序时,冗余地重新注册该处理程序,并且
  • Non-redundantly and successfully registering the specific handlers of WinType . 非冗余并成功注册WinType的特定处理程序。

The only part of all this that each window type actually, but unavoidably, needs to do is the last. 所有窗口类型实际上(但不可避免地)需要做的所有事情的唯一部分是最后一个。 So to your two questions: 因此,对于您的两个问题:

  1. Is it possible to do what I am trying to do without making the event handlers public? 是否可以在不公开事件处理程序的情况下做我想做的事情?

Yes. 是。 Just have each window-type register its own handlers. 只需让每个窗口类型注册其自己的处理程序即可。

  1. Is it also possible to avoid re-registering event handlers if the base class already did it? 如果基类已经做过,是否还可以避免重新注册事件处理程序?

Yes. 是。 Same answer. 相同的答案。

I appreciate that you are merely experimenting, but this experiment is not promising. 我很高兴您只是在进行实验,但是这个实验没有希望。

You attached a third question to question 2: 您在问题2上附加了第三个问题:

Is there a way to tell if the function was declared in the base class or if it originated in the derived class 有没有办法判断该函数是在基类中声明的还是它是在派生类中起源的

Yes. 是。 If you consult again this answer , and then also consult my answer to the same question just below, you will see that the first solution detects a member function T::mf when mf is defined in T but not when mf is inherited by T , whereas my solution detects mf in either case. 如果再次请教这个答案 ,然后还要请教我的回答同样的问题,只是下面,你将看到第一个解决方案检测的成员函数T::mf时, mf在定义T ,但不是当mf被继承T ,而我的解决方案在两种情况下均检测到mf Thus you can SFINAE-probe for T::mf by both methods and will know that mf is inherited if the first method does not detect it and the second method does. 因此,您可以通过这两种方法对T::mf进行SFINAE-probe探测,并且如果第一个方法未检测到它,而第二个方法则检测到了mf则它将知道mf是继承的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM