checked C++ bindings: only require boolean to be checked for errors once
authorSven Verdoolaege <sven@cerebras.net>
Wed, 9 Jan 2019 10:19:55 +0000 (9 11:19 +0100)
committerSven Verdoolaege <sven.verdoolaege@gmail.com>
Mon, 28 Jan 2019 08:28:38 +0000 (28 09:28 +0100)
Commit isl-0.18-1075-gb0afb84ca (checked C++ bindings:
abort on unchecked boolean error, Sat Jun 23 16:56:00 2018 +0200)
introduced a mechanism for ensuring that a boolean value
is checked at least once for errors.  However, if these values
get copied around (which may happen beyond the user's control),
then each of these copies would have to be checked.
While it would be possible to introduce a copy constructor and
an assignment operator that transfer the checking responsibility
to the copy, it seems cleaner to have these copies share
their state of having been checked for errors such that the user
is free to check any copy.
To this same end, the ! operator is modified to share the checking state
between the original boolean and its negation.
(The original implementation would mark the original value
as having been checked and the negation as not having been checked,
even if the original value had already been checked by that time.)

Do the same for isl::checked::stat and isl::checked::size
for consistency.  The issue is presumably less pressing
for isl::checked::stat since such values are less likely
to be copied.

Signed-off-by: Sven Verdoolaege <sven@cerebras.net>
cpp/cpp-checked.h.pre

index 4fa7717..988bedb 100644 (file)
@@ -24,9 +24,19 @@ namespace checked {
     abort();                                                 \
   } while (0)
 
+/* Class used to check that isl::checked::boolean,
+ * isl::checked::stat and isl::checked::size values are checked for errors.
+ */
+struct checker {
+       bool checked = false;
+       ~checker() {
+               ISLPP_ASSERT(checked, "IMPLEMENTATION ERROR: Unchecked state");
+       }
+};
+
 class boolean {
 private:
-  mutable bool checked = false;
+  mutable std::shared_ptr<checker> check = std::make_shared<checker>();
   isl_bool val;
 
   friend boolean manage(isl_bool val);
@@ -37,9 +47,6 @@ public:
   }
   boolean()
       : val(isl_bool_error) {}
-  ~boolean() {
-    ISLPP_ASSERT(checked, "IMPLEMENTATION ERROR: Unchecked state");
-  }
 
   /* implicit */ boolean(bool val)
       : val(val ? isl_bool_true : isl_bool_false) {}
@@ -47,24 +54,30 @@ public:
   isl_bool release() {
     auto tmp = val;
     val = isl_bool_error;
-    checked = true;
+    check->checked = true;
     return tmp;
   }
 
-  bool is_error() const { checked = true; return val == isl_bool_error; }
-  bool is_false() const { checked = true; return val == isl_bool_false; }
-  bool is_true() const { checked = true; return val == isl_bool_true; }
+  bool is_error() const { check->checked = true; return val == isl_bool_error; }
+  bool is_false() const { check->checked = true; return val == isl_bool_false; }
+  bool is_true() const { check->checked = true; return val == isl_bool_true; }
 
   explicit operator bool() const {
-    ISLPP_ASSERT(checked, "IMPLEMENTATION ERROR: Unchecked error state");
+    ISLPP_ASSERT(check->checked, "IMPLEMENTATION ERROR: Unchecked error state");
     ISLPP_ASSERT(!is_error(), "IMPLEMENTATION ERROR: Unhandled error state");
     return is_true();
   }
 
+  boolean negate() {
+    if (val == isl_bool_true)
+      val = isl_bool_false;
+    else if (val == isl_bool_false)
+      val = isl_bool_true;
+    return *this;
+  }
+
   boolean operator!() const {
-    if (is_error())
-      return *this;
-    return !is_true();
+    return boolean(*this).negate();
   }
 };
 
@@ -91,7 +104,7 @@ public:
  */
 class stat {
 private:
-       mutable bool checked = false;
+       mutable std::shared_ptr<checker> check = std::make_shared<checker>();
        isl_stat val;
 
        friend stat manage(isl_stat val);
@@ -104,21 +117,18 @@ public:
                return stat(isl_stat_error);
        }
        stat() : val(isl_stat_error) {}
-       ~stat() {
-               ISLPP_ASSERT(checked, "IMPLEMENTATION ERROR: Unchecked state");
-       }
 
        isl_stat release() {
-               checked = true;
+               check->checked = true;
                return val;
        }
 
        bool is_error() const {
-               checked = true;
+               check->checked = true;
                return val == isl_stat_error;
        }
        bool is_ok() const {
-               checked = true;
+               check->checked = true;
                return val == isl_stat_ok;
        }
 };
@@ -132,31 +142,28 @@ inline stat manage(isl_stat val)
  */
 class size {
 private:
-       mutable bool checked = false;
+       mutable std::shared_ptr<checker> check = std::make_shared<checker>();
        isl_size val;
 
        friend size manage(isl_size val);
        size(isl_size val) : val(val) {}
 public:
        size() : val(isl_size_error) {}
-       ~size() {
-               ISLPP_ASSERT(checked, "IMPLEMENTATION ERROR: Unchecked state");
-       }
 
        isl_size release() {
                auto tmp = val;
                val = isl_size_error;
-               checked = true;
+               check->checked = true;
                return tmp;
        }
 
        bool is_error() const {
-               checked = true;
+               check->checked = true;
                return val == isl_size_error;
        }
 
        explicit operator unsigned() const {
-               ISLPP_ASSERT(checked,
+               ISLPP_ASSERT(check->checked,
                            "IMPLEMENTATION ERROR: Unchecked error state");
                ISLPP_ASSERT(!is_error(),
                            "IMPLEMENTATION ERROR: Unhandled error state");