Minxin is kind of extension of a class for new feature with default implementation. See this example from StackOverflow.
#include <iostream>
using namespace std;
struct Number{
typedef int value_type; value_type n;
void set(value_type v) { n = v; }
int get() const { return n; }
};
template <typename BASE, typename T = typename BASE::value_type>
struct Undoable : public BASE{
typedef T value_type; T before;
void set(T v) { before = BASE::get(); BASE::set(v); }
void undo() { BASE::set(before); }
};
template <typename BASE, typename T = typename BASE::value_type>
struct Redoable : public BASE{
typedef T value_type; T after;
void set(T v) { after = v; BASE::set(v); }
void redo() { BASE::set(after); }
};
typedef Redoable< Undoable<Number> > ReUndoableNumber;
int main(){
ReUndoableNumber mynum; mynum.set(42); mynum.set(84);
cout << mynum.get() << '\n'; // 84
mynum.undo();
cout << mynum.get() << '\n'; // 42
mynum.redo();
cout << mynum.get() << '\n'; // back to 84
}
We have a base type Number, and we have two new features of Undoable and Redoable, which is not specific for any particular types, but with get(), set() method. Moreover, Undoable and Redoable are called Mixins, and Number is called regular class. Mixins can be combined with other Mixins and regular class. Because of Mixin's composibility, it is a class template which inherits its template type arguments for composition. Therefore Mixin will impose some implicit requirement for its template type arguments by using template type argument to extend new feature, like redo(), undo() above.
In addition, because of Inheritance, we can cast Outer Mixin to Inter Mixin or Base regular class implicitly.
With CRTP, which pass Derived class to base class as template argument, in order to let Base class know what is Derived class, we can cast Inter classes in the Mixin chain to outer classes.
To sum up, with Mixin and CRTP, we can incrementally extends regular class with new features and cast back and forth in the inheritance chain.
In LLVM, we can see CRTP mix-in pattern as well, but written in another way. If some regular class is derived from Mixin, it gets features from that Mixin. Moreover, Mixins, in this manner, are chained by inheriting another Mixin to accumulate feature set. See following code:
/* /wtsc/llvm-project/llvm/include/llvm/IR/PassManager.h */
...
/// A CRTP mix-in to automatically provide informational APIs needed for
/// passes.
///
/// This provides some boilerplate for types that are passes.
template <typename DerivedT> struct PassInfoMixin {
/// Gets the name of the pass we are mixed into.
static StringRef name() {
static_assert(std::is_base_of<PassInfoMixin, DerivedT>::value,
"Must pass the derived type as the template argument!");
StringRef Name = getTypeName<DerivedT>();
if (Name.startswith("llvm::"))
Name = Name.drop_front(strlen("llvm::"));
return Name;
}
};
/// A CRTP mix-in that provides informational APIs needed for analysis passes.
///
/// This provides some boilerplate for types that are analysis passes. It
/// automatically mixes in \c PassInfoMixin.
template <typename DerivedT>
struct AnalysisInfoMixin : PassInfoMixin<DerivedT> {
/// Returns an opaque, unique ID for this analysis type.
///
/// This ID is a pointer type that is guaranteed to be 8-byte aligned and thus
/// suitable for use in sets, maps, and other data structures that use the low
/// bits of pointers.
///
/// Note that this requires the derived type provide a static \c AnalysisKey
/// member called \c Key.
///
/// FIXME: The only reason the mixin type itself can't declare the Key value
/// is that some compilers cannot correctly unique a templated static variable
/// so it has the same addresses in each instantiation. The only currently
/// known platform with this limitation is Windows DLL builds, specifically
/// building each part of LLVM as a DLL. If we ever remove that build
/// configuration, this mixin can provide the static key as well.
static AnalysisKey *ID() {
static_assert(std::is_base_of<AnalysisInfoMixin, DerivedT>::value,
"Must pass the derived type as the template argument!");
return &DerivedT::Key;
}
...
The first Mixin is PassInfoMixin which adds a feature to get type name of the Derived class. The second Mixin is AnalysisInfoMixin which adds another feature to get the Pass ID. They both use inheritance to add more features to specific class, i.e. the regular class.
Now we can see how interesting Mixin pattern is.