From f6158bc3431ea9b431ca7b1367eb5ae4f5195308 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 10 Feb 2020 13:14:08 +0100 Subject: [PATCH 1/4] Remove wxHAS_PAINT_DEBUG and code guarded by it This doesn't seem to actually be doing anything useful, as it only checks that wxPaintDCImpl is not created without creating wxPaintDC which is impossible to do accidentally anyhow. This will be replaced by a more useful check in the next commit. --- include/wx/event.h | 26 +------------------------- src/msw/dcclient.cpp | 21 --------------------- 2 files changed, 1 insertion(+), 46 deletions(-) diff --git a/include/wx/event.h b/include/wx/event.h index 482758b7a2..293211aae3 100644 --- a/include/wx/event.h +++ b/include/wx/event.h @@ -2335,39 +2335,15 @@ private: wxEVT_NC_PAINT */ -#if wxDEBUG_LEVEL && defined(__WXMSW__) - #define wxHAS_PAINT_DEBUG - - // see comments in src/msw/dcclient.cpp where g_isPainting is defined - extern WXDLLIMPEXP_CORE int g_isPainting; -#endif // debug - class WXDLLIMPEXP_CORE wxPaintEvent : public wxEvent { public: wxPaintEvent(int Id = 0) : wxEvent(Id, wxEVT_PAINT) { -#ifdef wxHAS_PAINT_DEBUG - // set the internal flag for the duration of redrawing - g_isPainting++; -#endif // debug } - // default copy ctor and dtor are normally fine, we only need them to keep - // g_isPainting updated in debug build -#ifdef wxHAS_PAINT_DEBUG - wxPaintEvent(const wxPaintEvent& event) - : wxEvent(event) - { - g_isPainting++; - } - - virtual ~wxPaintEvent() - { - g_isPainting--; - } -#endif // debug + // default copy ctor and dtor are fine virtual wxEvent *Clone() const wxOVERRIDE { return new wxPaintEvent(*this); } diff --git a/src/msw/dcclient.cpp b/src/msw/dcclient.cpp index ed6fcf3e9e..2b628ef97d 100644 --- a/src/msw/dcclient.cpp +++ b/src/msw/dcclient.cpp @@ -138,18 +138,6 @@ PaintDCInfos gs_PaintDCInfos; } // anonymous namespace -// ---------------------------------------------------------------------------- -// global variables -// ---------------------------------------------------------------------------- - -#ifdef wxHAS_PAINT_DEBUG - // a global variable which we check to verify that wxPaintDC are only - // created in response to WM_PAINT message - doing this from elsewhere is a - // common programming error among wxWidgets programmers and might lead to - // very subtle and difficult to debug refresh/repaint bugs. - int g_isPainting = 0; -#endif // wxHAS_PAINT_DEBUG - // =========================================================================== // implementation // =========================================================================== @@ -269,15 +257,6 @@ wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *window ) : { wxCHECK_RET( window, wxT("NULL canvas in wxPaintDCImpl ctor") ); -#ifdef wxHAS_PAINT_DEBUG - if ( g_isPainting <= 0 ) - { - wxFAIL_MSG( wxT("wxPaintDCImpl may be created only in EVT_PAINT handler!") ); - - return; - } -#endif // wxHAS_PAINT_DEBUG - // see comments in src/msw/window.cpp where this is defined extern wxStack wxDidCreatePaintDC; From 452c8dcfa3c8864edaa594520c6f1a1780014072 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 10 Feb 2020 13:39:08 +0100 Subject: [PATCH 2/4] Check that wxPaintDC is created correctly in code using wxMSW Creating wxPaintDC for a window outside of any wxEVT_PAINT handler already resulted in assert failures and crash due to using the empty wxDidCreatePaintDC stack, but the assert message was not really clear, so improve it by stating explicitly that wxPaintDC can only be created from wxEVT_PAINT handlers. Also check that wxPaintDC is being created for the correct window: this wasn't detected at all before, but could still result in a lot of grief, so check for this too. Finally, create a new private header with the paint data stack variable declaration instead of using "extern" to declare it manually in wxDC code. --- include/wx/msw/private/paint.h | 39 ++++++++++++++++++++++++++++++++++ src/msw/dcclient.cpp | 11 ++++++---- src/msw/window.cpp | 20 +++++++++-------- 3 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 include/wx/msw/private/paint.h diff --git a/include/wx/msw/private/paint.h b/include/wx/msw/private/paint.h new file mode 100644 index 0000000000..abd0c44cd1 --- /dev/null +++ b/include/wx/msw/private/paint.h @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/msw/private/paint.h +// Purpose: Helpers for handling repainting +// Author: Vadim Zeitlin +// Created: 2020-02-10 +// Copyright: (c) 2020 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_MSW_PRIVATE_PAINT_H_ +#define _WX_MSW_PRIVATE_PAINT_H_ + +#include "wx/stack.h" + +namespace wxMSWImpl +{ + +// Data used by WM_PAINT handler +struct PaintData +{ + explicit PaintData(wxWindowMSW* window_) + : window(window_), + createdPaintDC(false) + { + } + + // The window being repainted (never null). + wxWindowMSW* const window; + + // True if the user-defined paint handler created wxPaintDC. + bool createdPaintDC; +}; + +// Stack storing data for the possibly nested WM_PAINT handlers. +extern wxStack paintStack; + +} // namespace wxMSWImpl + +#endif // _WX_MSW_PRIVATE_PAINT_H_ diff --git a/src/msw/dcclient.cpp b/src/msw/dcclient.cpp index 2b628ef97d..d178e0e7ed 100644 --- a/src/msw/dcclient.cpp +++ b/src/msw/dcclient.cpp @@ -36,6 +36,7 @@ #include "wx/stack.h" #include "wx/msw/private.h" +#include "wx/msw/private/paint.h" // ---------------------------------------------------------------------------- // local data structures @@ -257,11 +258,13 @@ wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *window ) : { wxCHECK_RET( window, wxT("NULL canvas in wxPaintDCImpl ctor") ); - // see comments in src/msw/window.cpp where this is defined - extern wxStack wxDidCreatePaintDC; - - wxDidCreatePaintDC.top() = true; + using namespace wxMSWImpl; + wxCHECK_RET( !paintStack.empty(), + "wxPaintDC can't be created outside wxEVT_PAINT handler" ); + wxCHECK_RET( paintStack.top().window == window, + "wxPaintDC must be associated with the window being repainted" ); + paintStack.top().createdPaintDC = true; m_window = window; diff --git a/src/msw/window.cpp b/src/msw/window.cpp index f207a8afd7..0d06fcf9a5 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -63,7 +63,6 @@ #include "wx/popupwin.h" #include "wx/power.h" #include "wx/scopeguard.h" -#include "wx/stack.h" #include "wx/sysopt.h" #if wxUSE_DRAG_AND_DROP @@ -81,6 +80,7 @@ #include "wx/msw/private.h" #include "wx/msw/private/keyboard.h" +#include "wx/msw/private/paint.h" #include "wx/msw/private/winstyle.h" #include "wx/msw/dcclient.h" #include "wx/msw/seh.h" @@ -5240,8 +5240,7 @@ wxColour wxWindowMSW::MSWGetThemeColour(const wchar_t *themeName, // endless stream of WM_PAINT messages for this window resulting in a lot of // difficult to debug problems (e.g. impossibility to repaint other windows, // lack of timer and idle events and so on) -extern wxStack wxDidCreatePaintDC; -wxStack wxDidCreatePaintDC; +wxStack wxMSWImpl::paintStack; bool wxWindowMSW::HandlePaint() { @@ -5257,14 +5256,17 @@ bool wxWindowMSW::HandlePaint() m_updateRegion = wxRegion((WXHRGN) hRegion); - wxDidCreatePaintDC.push(false); + using namespace wxMSWImpl; + + paintStack.push(PaintData(this)); wxPaintEvent event(m_windowId); event.SetEventObject(this); bool processed = HandleWindowEvent(event); - if ( wxDidCreatePaintDC.top() && !processed ) + const bool createdPaintDC = paintStack.top().createdPaintDC; + if ( createdPaintDC && !processed ) { // Event handler did paint something as wxPaintDC object was created // but then it must have skipped the event to indicate that default @@ -5288,16 +5290,16 @@ bool wxWindowMSW::HandlePaint() wxPaintDCImpl::EndPaint((wxWindow *)this); + paintStack.pop(); + // It doesn't matter whether the event was actually processed or not here, // what matters is whether we already painted, and hence validated, the // window or not. If we did, either the event was processed or we called // OnPaint() above, so we should return true. If we did not, even the event // was processed, we must still call MSWDefWindowProc() to ensure that the // window is validated, i.e. to avoid the problem described in the comment - // before wxDidCreatePaintDC definition above. - const bool ret = wxDidCreatePaintDC.top(); - wxDidCreatePaintDC.pop(); - return ret; + // before paintStack definition above. + return createdPaintDC; } // Can be called from an application's OnPaint handler From b680ba9596ace0f8e7e486a276975d4e92edea5c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 10 Feb 2020 13:42:03 +0100 Subject: [PATCH 3/4] Explicitly document that wxPaintEvent ctor must not be used Objects of this type are only supposed to be created by wxWidgets itself and not by user code. --- interface/wx/event.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/wx/event.h b/interface/wx/event.h index c02abdff1c..35b97325df 100644 --- a/interface/wx/event.h +++ b/interface/wx/event.h @@ -2215,7 +2215,12 @@ class wxPaintEvent : public wxEvent { public: /** - Constructor. + Constructor for exclusive use of wxWidgets itself. + + Note that the objects of this class can @em not be created from + application code, they're only created by the library itself. If you + need a window to be repainted, use wxWindow::Refresh() instead of + trying to manually create an event of this class. */ wxPaintEvent(int id = 0); }; From 8fcf46f65c6a6bd9c4da303a464ddcea95323d3a Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 10 Feb 2020 14:08:53 +0100 Subject: [PATCH 4/4] Forbid creation of wxPaintEvent objects from user code This doesn't work anyhow, so it's better to prevent the code doing this from compiling instead of getting run-time asserts or worse. Also simplify construction of these events inside wxWidgets by passing the window itself to the ctor instead of passing just its ID and calling SetEventObject() separately later. For consistency, do the same thing for wxNcPaintEvent too. --- docs/changes.txt | 4 ++++ include/wx/event.h | 19 ++++++++++++------- interface/wx/event.h | 2 +- src/common/event.cpp | 16 ++++++++++++++++ src/dfb/window.cpp | 6 ++---- src/gtk/window.cpp | 6 ++---- src/gtk1/glcanvas.cpp | 6 ++---- src/gtk1/window.cpp | 6 ++---- src/motif/window.cpp | 3 +-- src/msw/window.cpp | 6 ++---- src/osx/window_osx.cpp | 6 ++---- src/qt/glcanvas.cpp | 2 +- src/qt/window.cpp | 3 +-- src/univ/topluniv.cpp | 3 +-- src/x11/window.cpp | 6 ++---- tests/events/evthandler.cpp | 11 +++++++++++ tests/test.bkl | 6 +++--- 17 files changed, 65 insertions(+), 46 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 63a155fcbc..8925b02258 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -103,6 +103,10 @@ Changes in behaviour which may result in build errors then you will need to add these libraries to your make or project files yourself. +- wxPaintEvent objects can no longer be created by the application code. This + was never supposed to work and is now forbidden at compile-time instead of + resulting in errors during run-time. + - WXWIN_OS_DESCRIPTION doesn't exist any longer, use wxGetOsDescription(). - Never documented and not always available private wxGetClipboardData() diff --git a/include/wx/event.h b/include/wx/event.h index 293211aae3..39ca40725d 100644 --- a/include/wx/event.h +++ b/include/wx/event.h @@ -2337,12 +2337,14 @@ private: class WXDLLIMPEXP_CORE wxPaintEvent : public wxEvent { + // This ctor is only intended to be used by wxWidgets itself, so it's + // intentionally declared as private when not building the library itself. +#ifdef WXBUILDING public: - wxPaintEvent(int Id = 0) - : wxEvent(Id, wxEVT_PAINT) - { - } +#endif // WXBUILDING + explicit wxPaintEvent(wxWindowBase* window = NULL); +public: // default copy ctor and dtor are fine virtual wxEvent *Clone() const wxOVERRIDE { return new wxPaintEvent(*this); } @@ -2353,11 +2355,14 @@ private: class WXDLLIMPEXP_CORE wxNcPaintEvent : public wxEvent { + // This ctor is only intended to be used by wxWidgets itself, so it's + // intentionally declared as private when not building the library itself. +#ifdef WXBUILDING public: - wxNcPaintEvent(int winid = 0) - : wxEvent(winid, wxEVT_NC_PAINT) - { } +#endif // WXBUILDING + explicit wxNcPaintEvent(wxWindowBase* window = NULL); +public: virtual wxEvent *Clone() const wxOVERRIDE { return new wxNcPaintEvent(*this); } private: diff --git a/interface/wx/event.h b/interface/wx/event.h index 35b97325df..7f9c0e9d66 100644 --- a/interface/wx/event.h +++ b/interface/wx/event.h @@ -2222,7 +2222,7 @@ public: need a window to be repainted, use wxWindow::Refresh() instead of trying to manually create an event of this class. */ - wxPaintEvent(int id = 0); + explicit wxPaintEvent(wxWindow* window); }; diff --git a/src/common/event.cpp b/src/common/event.cpp index e453b9694e..2d7b822c38 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -458,6 +458,22 @@ wxString wxCommandEvent::GetString() const return m_cmdString; } +// ---------------------------------------------------------------------------- +// wxPaintEvent and wxNcPaintEvent +// ---------------------------------------------------------------------------- + +wxPaintEvent::wxPaintEvent(wxWindowBase* window) + : wxEvent(window ? window->GetId() : 0, wxEVT_PAINT) +{ + SetEventObject(window); +} + +wxNcPaintEvent::wxNcPaintEvent(wxWindowBase* window) + : wxEvent(window ? window->GetId() : 0, wxEVT_NC_PAINT) +{ + SetEventObject(window); +} + // ---------------------------------------------------------------------------- // wxUpdateUIEvent // ---------------------------------------------------------------------------- diff --git a/src/dfb/window.cpp b/src/dfb/window.cpp index 888cf7491f..dd6a310f09 100644 --- a/src/dfb/window.cpp +++ b/src/dfb/window.cpp @@ -684,8 +684,7 @@ void wxWindowDFB::PaintWindow(const wxRect& rect) // only send wxNcPaintEvent if drawing at least part of nonclient area: if ( !clientRect.Contains(rect) ) { - wxNcPaintEvent eventNc(GetId()); - eventNc.SetEventObject(this); + wxNcPaintEvent eventNc(this); HandleWindowEvent(eventNc); } else @@ -697,8 +696,7 @@ void wxWindowDFB::PaintWindow(const wxRect& rect) // only send wxPaintEvent if drawing at least part of client area: if ( rect.Intersects(clientRect) ) { - wxPaintEvent eventPt(GetId()); - eventPt.SetEventObject(this); + wxPaintEvent eventPt(this); HandleWindowEvent(eventPt); } else diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index c85fe9fbb1..c00031bbb5 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -5205,12 +5205,10 @@ void wxWindowGTK::GTKSendPaintEvents(const GdkRegion* region) wxFAIL_MSG( "unsupported background style" ); } - wxNcPaintEvent nc_paint_event( GetId() ); - nc_paint_event.SetEventObject( this ); + wxNcPaintEvent nc_paint_event( this ); HandleWindowEvent( nc_paint_event ); - wxPaintEvent paint_event( GetId() ); - paint_event.SetEventObject( this ); + wxPaintEvent paint_event( this ); HandleWindowEvent( paint_event ); #if wxGTK_HAS_COMPOSITING_SUPPORT diff --git a/src/gtk1/glcanvas.cpp b/src/gtk1/glcanvas.cpp index b75b3766d8..17ab11450f 100644 --- a/src/gtk1/glcanvas.cpp +++ b/src/gtk1/glcanvas.cpp @@ -65,8 +65,7 @@ extern "C" { static gint gtk_glwindow_map_callback( GtkWidget * WXUNUSED(widget), wxGLCanvas *win ) { - wxPaintEvent event( win->GetId() ); - event.SetEventObject( win ); + wxPaintEvent event( win ); win->HandleWindowEvent( event ); win->GetUpdateRegion().Clear(); @@ -302,8 +301,7 @@ void wxGLCanvas::OnInternalIdle() { if (!m_updateRegion.IsEmpty()) { - wxPaintEvent event( GetId() ); - event.SetEventObject( this ); + wxPaintEvent event( this ); HandleWindowEvent( event ); GetUpdateRegion().Clear(); diff --git a/src/gtk1/window.cpp b/src/gtk1/window.cpp index 98224b25fb..55a266b9f5 100644 --- a/src/gtk1/window.cpp +++ b/src/gtk1/window.cpp @@ -3599,12 +3599,10 @@ void wxWindowGTK::GtkSendPaintEvents() m_clearRegion.Clear(); } - wxNcPaintEvent nc_paint_event( GetId() ); - nc_paint_event.SetEventObject( this ); + wxNcPaintEvent nc_paint_event( this ); HandleWindowEvent( nc_paint_event ); - wxPaintEvent paint_event( GetId() ); - paint_event.SetEventObject( this ); + wxPaintEvent paint_event( this ); HandleWindowEvent( paint_event ); m_clipPaintRegion = false; diff --git a/src/motif/window.cpp b/src/motif/window.cpp index a3bb7b274f..023770f093 100644 --- a/src/motif/window.cpp +++ b/src/motif/window.cpp @@ -1621,8 +1621,7 @@ void wxWindow::DoPaint() eraseEvent.SetEventObject(this); HandleWindowEvent(eraseEvent); - wxPaintEvent event(GetId()); - event.SetEventObject(this); + wxPaintEvent event(this); HandleWindowEvent(event); m_needsRefresh = false; diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 0d06fcf9a5..2572ec0efc 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -5260,8 +5260,7 @@ bool wxWindowMSW::HandlePaint() paintStack.push(PaintData(this)); - wxPaintEvent event(m_windowId); - event.SetEventObject(this); + wxPaintEvent event(this); bool processed = HandleWindowEvent(event); @@ -5280,8 +5279,7 @@ bool wxWindowMSW::HandlePaint() // note that we must generate NC event after the normal one as otherwise // BeginPaint() will happily overwrite our decorations with the background // colour - wxNcPaintEvent eventNc(m_windowId); - eventNc.SetEventObject(this); + wxNcPaintEvent eventNc(this); HandleWindowEvent(eventNc); // don't keep an HRGN we don't need any longer (GetUpdateRegion() can only diff --git a/src/osx/window_osx.cpp b/src/osx/window_osx.cpp index 4eaa5a907c..1e58f4c1ab 100644 --- a/src/osx/window_osx.cpp +++ b/src/osx/window_osx.cpp @@ -1989,9 +1989,8 @@ bool wxWindowMac::MacDoRedraw( long time ) { // paint the window itself - wxPaintEvent event(GetId()); + wxPaintEvent event(this); event.SetTimestamp(time); - event.SetEventObject(this); handled = HandleWindowEvent(event); } @@ -2040,8 +2039,7 @@ void wxWindowMac::MacPaintChildrenBorders() if ( m_updateRegion.Contains(clientOrigin.x+x-10, clientOrigin.y+y-10, w+20, h+20) ) { // paint custom borders - wxNcPaintEvent eventNc( child->GetId() ); - eventNc.SetEventObject( child ); + wxNcPaintEvent eventNc( child ); if ( !child->HandleWindowEvent( eventNc ) ) { child->MacPaintBorders(0, 0) ; diff --git a/src/qt/glcanvas.cpp b/src/qt/glcanvas.cpp index 60be7159f9..f89645c014 100644 --- a/src/qt/glcanvas.cpp +++ b/src/qt/glcanvas.cpp @@ -62,7 +62,7 @@ void wxQtGLWidget::resizeGL(int w, int h) void wxQtGLWidget::paintGL() { - wxPaintEvent event( GetHandler()->GetId() ); + wxPaintEvent event( GetHandler() ); EmitEvent(event); } diff --git a/src/qt/window.cpp b/src/qt/window.cpp index 5e1ac194ef..e2ac004a2b 100644 --- a/src/qt/window.cpp +++ b/src/qt/window.cpp @@ -1281,8 +1281,7 @@ bool wxWindowQt::QtHandlePaintEvent ( QWidget *handler, QPaintEvent *event ) } // send the paint event (wxWindowDC will draw directly): - wxPaintEvent paint( GetId() ); - paint.SetEventObject(this); + wxPaintEvent paint( this ); handled = ProcessWindowEvent(paint); m_updateRegion.Clear(); } diff --git a/src/univ/topluniv.cpp b/src/univ/topluniv.cpp index 312ce6a912..460f465892 100644 --- a/src/univ/topluniv.cpp +++ b/src/univ/topluniv.cpp @@ -216,8 +216,7 @@ long wxTopLevelWindow::GetDecorationsStyle() const void wxTopLevelWindow::RefreshTitleBar() { - wxNcPaintEvent event(GetId()); - event.SetEventObject(this); + wxNcPaintEvent event(this); GetEventHandler()->ProcessEvent(event); } diff --git a/src/x11/window.cpp b/src/x11/window.cpp index 373151892a..ddcd303511 100644 --- a/src/x11/window.cpp +++ b/src/x11/window.cpp @@ -1282,8 +1282,7 @@ void wxWindowX11::SendPaintEvents() m_clipPaintRegion = true; - wxPaintEvent paint_event( GetId() ); - paint_event.SetEventObject( this ); + wxPaintEvent paint_event( this ); HandleWindowEvent( paint_event ); m_updateRegion.Clear(); @@ -1324,8 +1323,7 @@ void wxWindowX11::SendNcPaintEvents() } } - wxNcPaintEvent nc_paint_event( GetId() ); - nc_paint_event.SetEventObject( this ); + wxNcPaintEvent nc_paint_event( this ); HandleWindowEvent( nc_paint_event ); m_updateNcArea = false; diff --git a/tests/events/evthandler.cpp b/tests/events/evthandler.cpp index a2ef903dbb..6be6c2d1ec 100644 --- a/tests/events/evthandler.cpp +++ b/tests/events/evthandler.cpp @@ -534,3 +534,14 @@ public: void OnIdle(wxIdleEvent&) { } }; #endif // C++11 + +// Another compilation-time-only test, but this one checking that these event +// objects can't be created from outside of the library. +#ifdef TEST_INVALID_EVENT_CREATION + +void TestEventCreation() +{ + wxPaintEvent eventPaint; +} + +#endif // TEST_INVALID_EVENT_CREATION diff --git a/tests/test.bkl b/tests/test.bkl index 70326b8dce..00f0c0b013 100644 --- a/tests/test.bkl +++ b/tests/test.bkl @@ -365,9 +365,9 @@ failtest_combobox: failtest_evthandler: @$(RM) test_evthandler.o - @for d in GLOBAL STATIC METHOD FUNCTOR NO_HANDLER DERIVED WRONG_CLASS; do \ - if $(MAKE) CXXWARNINGS=-DTEST_INVALID_BIND_$$d test_evthandler.o 2>/dev/null; then \ - echo "*** Compilation with TEST_INVALID_BIND_$$d unexpectedly succeeded.">&2; \ + @for d in BIND_GLOBAL BIND_STATIC BIND_METHOD BIND_FUNCTOR BIND_NO_HANDLER BIND_DERIVED BIND_WRONG_CLASS EVENT_CREATION; do \ + if $(MAKE) CXXWARNINGS=-DTEST_INVALID_$$d test_evthandler.o 2>/dev/null; then \ + echo "*** Compilation with TEST_INVALID_$$d unexpectedly succeeded.">&2; \ exit 1; \ fi; \ done; \