Register stack deleters automatically.

Rather than manually register the stack deleters separately, instantiate
them automatically from DEFINE_STACK_OF and BORINGSSL_MAKE_DELETER. The
StackTraits bridge in DEFINE_STACK_OF will additionally be used for
other C++ STACK_OF conveniences.

Bug: 132
Change-Id: I95d6c15b2219b34c7a8ce06dd8012d073dc19c27
Reviewed-on: https://boringssl-review.googlesource.com/18465
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/include/openssl/stack.h b/include/openssl/stack.h
index 5c4b506..8f15ea2 100644
--- a/include/openssl/stack.h
+++ b/include/openssl/stack.h
@@ -212,11 +212,38 @@
  * This set of macros is used to emit the typed functions that act on a
  * |STACK_OF(T)|. */
 
+#if !defined(BORINGSSL_NO_CXX)
+extern "C++" {
+namespace bssl {
+namespace internal {
+template <typename T>
+struct StackTraits {};
+}
+}
+}
+
+#define BORINGSSL_DEFINE_STACK_TRAITS(name, is_const) \
+  extern "C++" {                                      \
+  namespace bssl {                                    \
+  namespace internal {                                \
+  template <>                                         \
+  struct StackTraits<STACK_OF(name)> {                \
+    using Type = name;                                \
+    static constexpr bool kIsConst = is_const;        \
+  };                                                  \
+  }                                                   \
+  }                                                   \
+  }
+
+#else
+#define BORINGSSL_DEFINE_STACK_TRAITS(name, is_const)
+#endif
+
 /* Stack functions must be tagged unused to support file-local stack types.
  * Clang's -Wunused-function only allows unused static inline functions if they
  * are defined in a header. */
 
-#define DEFINE_STACK_OF_IMPL(name, ptrtype, constptrtype)                      \
+#define BORINGSSL_DEFINE_STACK_OF_IMPL(name, ptrtype, constptrtype)            \
   DECLARE_STACK_OF(name);                                                      \
                                                                                \
   typedef int (*stack_##name##_cmp_func)(constptrtype *a, constptrtype *b);    \
@@ -323,19 +350,22 @@
 
 /* DEFINE_STACK_OF defines |STACK_OF(type)| to be a stack whose elements are
  * |type| *. */
-#define DEFINE_STACK_OF(type) DEFINE_STACK_OF_IMPL(type, type *, const type *)
+#define DEFINE_STACK_OF(type) \
+  BORINGSSL_DEFINE_STACK_OF_IMPL(type, type *, const type *) \
+  BORINGSSL_DEFINE_STACK_TRAITS(type, false)
 
 /* DEFINE_CONST_STACK_OF defines |STACK_OF(type)| to be a stack whose elements
  * are const |type| *. */
 #define DEFINE_CONST_STACK_OF(type) \
-  DEFINE_STACK_OF_IMPL(type, const type *, const type *)
+  BORINGSSL_DEFINE_STACK_OF_IMPL(type, const type *, const type *) \
+  BORINGSSL_DEFINE_STACK_TRAITS(type, true)
 
 /* DEFINE_SPECIAL_STACK_OF defines |STACK_OF(type)| to be a stack whose elements
  * are |type|, where |type| must be a typedef for a pointer. */
 #define DEFINE_SPECIAL_STACK_OF(type)                          \
   OPENSSL_COMPILE_ASSERT(sizeof(type) == sizeof(void *),       \
                          special_stack_of_non_pointer_##type); \
-  DEFINE_STACK_OF_IMPL(type, type, const type)
+  BORINGSSL_DEFINE_STACK_OF_IMPL(type, type, const type)
 
 
 typedef char *OPENSSL_STRING;
@@ -348,4 +378,40 @@
 }  /* extern C */
 #endif
 
+#if !defined(BORINGSSL_NO_CXX)
+extern "C++" {
+
+#include <type_traits>
+
+namespace bssl {
+
+namespace internal {
+
+// Stacks defined with |DEFINE_CONST_STACK_OF| are freed with |sk_free|.
+template <typename Stack>
+struct DeleterImpl<
+    Stack, typename std::enable_if<StackTraits<Stack>::kIsConst>::type> {
+  static void Free(Stack *sk) { sk_free(reinterpret_cast<_STACK *>(sk)); }
+};
+
+// Stacks defined with |DEFINE_STACK_OF| are freed with |sk_pop_free| and the
+// corresponding type's deleter.
+template <typename Stack>
+struct DeleterImpl<
+    Stack, typename std::enable_if<!StackTraits<Stack>::kIsConst>::type> {
+  static void Free(Stack *sk) {
+    sk_pop_free(
+        reinterpret_cast<_STACK *>(sk),
+        reinterpret_cast<void (*)(void *)>(
+            DeleterImpl<typename StackTraits<Stack>::Type>::Free));
+  }
+};
+
+}  // namespace internal
+
+}  // namespace bssl
+
+}  // extern C++
+#endif
+
 #endif  /* OPENSSL_HEADER_STACK_H */