]> git.saurik.com Git - wxWidgets.git/commitdiff
added macros for checking for conflicts between flags values; use it in wxSizer code
authorVáclav Slavík <vslavik@fastmail.fm>
Tue, 4 Mar 2008 12:03:02 +0000 (12:03 +0000)
committerVáclav Slavík <vslavik@fastmail.fm>
Tue, 4 Mar 2008 12:03:02 +0000 (12:03 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@52310 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/private/flagscheck.h [new file with mode: 0644]
src/common/sizer.cpp

diff --git a/include/wx/private/flagscheck.h b/include/wx/private/flagscheck.h
new file mode 100644 (file)
index 0000000..751278e
--- /dev/null
@@ -0,0 +1,109 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        wx/private/flagscheck.h
+// Purpose:     helpers for checking that (bit)flags don't overlap
+// Author:      Vaclav Slavik
+// Created:     2008-02-21
+// RCS-ID:      $Id$
+// Copyright:   (c) 2008 Vaclav Slavik
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_PRIVATE_FLAGSCHECK_H_
+#define _WX_PRIVATE_FLAGSCHECK_H_
+
+#include "wx/debug.h"
+#include "wx/meta/if.h"
+
+namespace wxPrivate
+{
+
+// These templates are used to implement wxADD_FLAG macro below.
+//
+// The idea is that we want to trigger *compilation* error if the flags
+// overlap, not just runtime assert failure. We can't implement the check
+// using just a simple logical operation, we need checks equivalent to this
+// code:
+//
+//   mask = wxFLAG_1;
+//   assert( (mask & wxFLAG_2) == 0 ); // no overlap
+//   mask |= wxFLAG_3;
+//   assert( (mask & wxFLAG_3) == 0 ); // no overlap
+//   mask |= wxFLAG_3;
+//   ...
+//
+// This can be done at compilation time by using templates metaprogramming
+// technique that makes the compiler carry on the computation.
+//
+// NB: If any of this doesn't compile with your compiler and would be too
+//     hard to make work, it's probably best to disable this code and replace
+//     the macros below with empty stubs, this isn't anything criticial.
+
+template<int val> struct FlagsHaveConflictingValues
+{
+    // no value here - triggers compilation error
+};
+
+template<int val> struct FlagValue
+{
+    enum { value = val };
+};
+
+// This template adds its template parameter integer 'add' to another integer
+// 'all' and produces their OR-combination (all | add). The result is "stored"
+// as constant SafelyAddToMask<>::value. Combination of many flags is achieved
+// by chaining parameter lists: the 'add' parameter is value member of
+// another (different) SafelyAddToMask<> instantiation.
+template<int all, int add> struct SafelyAddToMask
+{
+    // This typedefs ensures that no flags in the list conflict. If there's
+    // any overlap between the already constructed part of the mask ('all')
+    // and the value being added to it ('add'), the test that is wxIf<>'s
+    // first parameter will be non-zero and so Added value will be
+    // FlagsHaveConflictingValues<add>. The next statement will try to use
+    // AddedValue::value, but there's no such thing in
+    // FlagsHaveConflictingValues<> and so compilation will fail.
+    typedef typename wxIf<(all & add) == 0,
+                          FlagValue<add>,
+                          FlagsHaveConflictingValues<add> >::value
+            AddedValue;
+
+    enum { value = all | AddedValue::value };
+};
+
+} // wxPrivate namespace
+
+
+
+// This macro is used to ensure that no two flags that can be combined in
+// the same integer value have overlapping bits. This is sometimes not entirely
+// trivial to ensure, for example in wxWindow styles or flags for wxSizerItem
+// that span several enums, some of them used for multiple purposes.
+//
+// By constructing allowed flags mask using wxADD_FLAG macro and then using
+// this mask to check flags passed as arguments, you can ensure that
+//
+// a) if any of the allowed flags overlap, you will get compilation error
+// b) if invalid flag is used, there will be an assert at runtime
+//
+// Example usage:
+//
+//   static const int SIZER_FLAGS_MASK =
+//       wxADD_FLAG(wxCENTRE,
+//       wxADD_FLAG(wxHORIZONTAL,
+//       wxADD_FLAG(wxVERTICAL,
+//       ...
+//       0))...);
+//
+// And wherever flags are used:
+//
+//   wxASSERT_VALID_FLAG( m_flag, SIZER_FLAGS_MASK );
+
+#define wxADD_FLAG(f, others) \
+    ::wxPrivate::SafelyAddToMask<f, others>::value
+
+// Checks if flags value 'f' is within the mask of allowed values
+#define wxASSERT_VALID_FLAGS(f, mask)                   \
+    wxASSERT_MSG( (f & mask) == f,                      \
+                  "invalid flag: not within " #mask )
+
+#endif // _WX_PRIVATE_FLAGSCHECK_H_
index 6d70ae0bc34a560a582e2581274b1d10f0a2fa5d..930aa061bbb11d047ca7479527eb05b24dcbb2f7 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "wx/display.h"
 #include "wx/sizer.h"
+#include "wx/private/flagscheck.h"
 
 #ifndef WX_PRECOMP
     #include "wx/string.h"
@@ -87,6 +88,31 @@ WX_DEFINE_EXPORTED_LIST( wxSizerItemList )
 // wxSizerItem
 // ----------------------------------------------------------------------------
 
+// check for flags conflicts
+static const int SIZER_FLAGS_MASK =
+    wxADD_FLAG(wxCENTRE,
+    wxADD_FLAG(wxHORIZONTAL,
+    wxADD_FLAG(wxVERTICAL,
+    wxADD_FLAG(wxLEFT,
+    wxADD_FLAG(wxRIGHT,
+    wxADD_FLAG(wxUP,
+    wxADD_FLAG(wxDOWN,
+    wxADD_FLAG(wxALIGN_NOT,
+    wxADD_FLAG(wxALIGN_CENTER_HORIZONTAL,
+    wxADD_FLAG(wxALIGN_RIGHT,
+    wxADD_FLAG(wxALIGN_BOTTOM,
+    wxADD_FLAG(wxALIGN_CENTER_VERTICAL,
+    wxADD_FLAG(wxFIXED_MINSIZE,
+    wxADD_FLAG(wxRESERVE_SPACE_EVEN_IF_HIDDEN,
+    wxADD_FLAG(wxSTRETCH_NOT,
+    wxADD_FLAG(wxSHRINK,
+    wxADD_FLAG(wxGROW,
+    wxADD_FLAG(wxSHAPED,
+    0))))))))))))))))));
+
+#define ASSERT_VALID_SIZER_FLAGS(f)  wxASSERT_VALID_FLAGS(f, SIZER_FLAGS_MASK)
+
+
 void wxSizerItem::Init(const wxSizerFlags& flags)
 {
     Init();
@@ -94,6 +120,8 @@ void wxSizerItem::Init(const wxSizerFlags& flags)
     m_proportion = flags.GetProportion();
     m_flag = flags.GetFlags();
     m_border = flags.GetBorderInPixels();
+
+    ASSERT_VALID_SIZER_FLAGS( m_flag );
 }
 
 wxSizerItem::wxSizerItem()
@@ -136,6 +164,8 @@ wxSizerItem::wxSizerItem(wxWindow *window,
              m_id(wxID_NONE),
              m_userData(userData)
 {
+    ASSERT_VALID_SIZER_FLAGS( m_flag );
+
     DoSetWindow(window);
 }
 
@@ -160,6 +190,8 @@ wxSizerItem::wxSizerItem(wxSizer *sizer,
              m_ratio(0.0),
              m_userData(userData)
 {
+    ASSERT_VALID_SIZER_FLAGS( m_flag );
+
     DoSetSizer(sizer);
 
     // m_minSize is set later
@@ -189,6 +221,8 @@ wxSizerItem::wxSizerItem(int width,
              m_id(wxID_NONE),
              m_userData(userData)
 {
+    ASSERT_VALID_SIZER_FLAGS( m_flag );
+
     DoSetSpacer(wxSize(width, height));
 }