Selaa lähdekoodia

Add documentation.

Sam Jaffe 5 vuotta sitten
vanhempi
commit
800f1ac3bb
1 muutettua tiedostoa jossa 73 lisäystä ja 0 poistoa
  1. 73 0
      include/opaque_typedef/opaque_typedef.hpp

+ 73 - 0
include/opaque_typedef/opaque_typedef.hpp

@@ -16,6 +16,48 @@
 #include "comparable.hpp"
 
 namespace types {
+  /**
+   * A class that allows you to perform a typedef without the risk of
+   * accidentally changing the meaning of a type due to negligence. This is
+   * useful instead of manually creating a wrapper class because it allows you
+   * to automatically generate the majority of interesting functions that the
+   * type needs to work without needing to handle boilerplate yourself. Suppose,
+   * for example, I have a collection of items that are indexed either in the
+   * local datastore, or in the external datastore. One option would be to use a
+   * 'tagged union', but this does nothing to allow someone to misuse the index
+   * at a lower point in code if we pass down only the index. We can't use a
+   * variant unless the index types are different, or we can use opaque_typedef:
+   * \code
+   * using local_index = opaque_typedef<int, struct local_index_t, ...>;
+   * using external_index = opaque_typedef<int, struct external_index_t, ...>;
+   * struct item {
+   *   std::variant<local_index, external_index> index;
+   * };
+   * \endcode
+   * This means that we can defer unwrapping the index until we leave the
+   * module, guaranteeing that we only use local_index for the local datastore
+   * code paths, and external_index for the external datastore's.
+   *
+   * Additional features are aquired primarily via mixins with the Skills
+   * template arguments. Provided skills are named similarly to Concepts:
+   * - EqualityComparable: can perform operator== and operator!=
+   * - Comparable: supports all 6 comparison operators
+   * - Incrementable: suports operator++ prefix and postfix
+   * - Decrementable: suports operator-- prefix and postfix
+   * - Addable: supports binary addition on itself
+   * - Arithmetic: supports binary addition and subtraction on itself, as well
+   * as unary negation
+   * - Numeric: supports binary addition, subtraction, multiplication, and
+   * division on itself, as well as unary negation
+   *
+   * An important distinction here is that you should not use opaque_typedef in
+   * situations where you need to provide non-default implementations of
+   * operations. For example, you would not make an opaque typedef to define a
+   * file path, because path + path should generally perform path concatenation,
+   * and thus may include an additional path.separator.
+   * However, it would be valid to use opaque typedefs to differentiate between,
+   * say, local files and remote files.
+   */
   template <typename Base, typename Tag, template <typename> class... Skills>
   class opaque_typedef
       : public Skills<opaque_typedef<Base, Tag, Skills...>>... {
@@ -23,13 +65,44 @@ namespace types {
     Base value_;
 
   public:
+    /**
+     * By necessity - opaque typedefs must provide a default constructor so that
+     * they can be used in containers and other contexts where we can't always
+     * provide direct construction.
+     */
     opaque_typedef() = default;
+    /**
+     * An opaque_typedef does not permit implicit conversion, because the
+     * purpose is to avoid accidentaly mixing up multiple arguments. For
+     * example: draw_rect(int x, int y, int w, int h) is easily mistakable,
+     * whereas draw_rect(position x, position y, length w, length h) is easier
+     * to reason about, and half as error-prone due to limitting the number of
+     * mixups
+     */
     explicit opaque_typedef(Base const & value) : value_(value){};
     explicit opaque_typedef(Base && value)
         : value_(std::forward<Base>(value)){};
+    /**
+     * Implicit conversion between multiple opaque_typedef types can be
+     * accomplished by defining this constructor for the two types. The weakness
+     * of this is that improper conversions will produce linker errors instead
+     * of compiler errors, but this is the cleanes implementation method. Create
+     * a converting constructor as follows:
+     * \code
+     * using TypeA = opaque_typedef<...>;
+     * using TypeB = opaque_typedef<...>;
+     *
+     * template <> template <>
+     * TypeA::opaque_typedef(TypeB const & other) { ... }
+     * \endcode
+     */
     template <typename B, typename T, template <typename> class... S>
     opaque_typedef(opaque_typedef<B, T, S...> const & other);
 
+    /**
+     * Explicit conversion for use in static_casts, or by invoking get()
+     * directly in your code.
+     */
     Base const & get() const { return value_; }
     explicit operator Base const &() const { return value_; }
   };