瀏覽代碼

Merge branch 'bug29805_rebased_squashed'

Nick Mathewson 5 年之前
父節點
當前提交
f32d890531
共有 2 個文件被更改,包括 99 次插入1 次删除
  1. 3 0
      changes/bug29805
  2. 96 1
      src/lib/math/prob_distr.h

+ 3 - 0
changes/bug29805

@@ -0,0 +1,3 @@
+  o Minor bugfixes (probability distributions):
+    - Refactor and improve parts of the probability distribution code that made
+      Coverity complain. Fixes bug 29805; bugfix on 0.4.0.1-alpha.

+ 96 - 1
src/lib/math/prob_distr.h

@@ -19,10 +19,100 @@ struct dist {
   const struct dist_ops *ops;
 };
 
+/**
+ * Untyped initializer element for struct dist using the specified
+ * struct dist_ops pointer.  Don't actually use this directly -- use
+ * the type-specific macro built out of DIST_BASE_TYPED below -- but if
+ * you did use this directly, it would be something like:
+ *
+ *   struct weibull mydist = {
+ *     DIST_BASE(&weibull_ops),
+ *     .lambda = ...,
+ *     .k = ...,
+ *   };
+ *
+ * Note there is NO COMPILER FEEDBACK if you accidentally do something
+ * like
+ *
+ *   struct geometric mydist = {
+ *     DIST_BASE(&weibull_ops),
+ *     ...
+ *   };
+ */
 #define DIST_BASE(OPS)  { .ops = (OPS) }
+
+/** A compile-time type-checking macro for use with DIST_BASE_TYPED.
+ *
+ *  This macro works by checking that &OBJ is a pointer type that is the same
+ *  type (except for qualifiers) as (const TYPE *)&OBJ. It's a C constraint
+ *  violation (which requires a diagnostic) if two pointers are different types
+ *  and are subtracted. The sizeof() forces compile-time evaluation, and the
+ *  multiplication by zero is to discard the result of the sizeof() from the
+ *  expression.
+ *
+ *  We define this conditionally to suppress false positives from
+ *  Coverity, which gets confused by the sizeof business.
+ */
+#ifdef __COVERITY___
+#define TYPE_CHECK_OBJ(OPS, OBJ, TYPE) 0
+#else
+#define TYPE_CHECK_OBJ(OPS, OBJ, TYPE) \
+  (0*sizeof(&(OBJ) - (const TYPE *)&(OBJ)))
+#endif
+
+/**
+* Typed initializer element for struct dist using the specified struct
+* dist_ops pointer.  Don't actually use this directly -- use a
+* type-specific macro built out of it -- but if you did use this
+* directly, it would be something like:
+*
+*   struct weibull mydist = {
+*     DIST_BASE_TYPED(&weibull_ops, mydist, struct weibull),
+*     .lambda = ...,
+*     .k = ...,
+*   };
+*
+* If you want to define a distribution type, define a canonical set of
+* operations and define a type-specific initializer element like so:
+*
+*   struct foo {
+*     struct dist base;
+*     int omega;
+*     double tau;
+*     double phi;
+*   };
+*
+*   struct dist_ops foo_ops = ...;
+*
+*   #define FOO(OBJ) DIST_BASE_TYPED(&foo_ops, OBJ, struct foo)
+*
+* Then users can do:
+*
+*   struct foo mydist = {
+*     FOO(mydist),
+*     .omega = ...,
+*     .tau = ...,
+*     .phi = ...,
+*   };
+*
+* If you accidentally write
+*
+*   struct bar mydist = {
+*     FOO(mydist),
+*     ...
+*   };
+*
+* then the compiler will report a type mismatch in the sizeof
+* expression, which otherwise evaporates at runtime.
+*/
 #define DIST_BASE_TYPED(OPS, OBJ, TYPE)                         \
-  DIST_BASE((OPS) + 0*sizeof(&(OBJ) - (const TYPE *)&(OBJ)))
+  DIST_BASE((OPS) + TYPE_CHECK_OBJ(OPS,OBJ,TYPE))
 
+/**
+ * Generic operations on distributions.  These simply defer to the
+ * corresponding dist_ops function.  In the parlance of C++, these call
+ * virtual member functions.
+ */
 const char *dist_name(const struct dist *);
 double dist_sample(const struct dist *);
 double dist_cdf(const struct dist *, double x);
@@ -30,6 +120,11 @@ double dist_sf(const struct dist *, double x);
 double dist_icdf(const struct dist *, double p);
 double dist_isf(const struct dist *, double p);
 
+/**
+ * Set of operations on a potentially parametric family of
+ * distributions.  In the parlance of C++, this would be called a
+ * `vtable' and the members are virtual member functions.
+ */
 struct dist_ops {
   const char *name;
   double (*sample)(const struct dist *);