Skip to content

Instantly share code, notes, and snippets.

@dboyliao
Last active March 16, 2022 09:33
Show Gist options
  • Select an option

  • Save dboyliao/95f827d584df1c8b66cb8d0fe55b6725 to your computer and use it in GitHub Desktop.

Select an option

Save dboyliao/95f827d584df1c8b66cb8d0fe55b6725 to your computer and use it in GitHub Desktop.
Simple C++ Example for CRTP
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <utility>
template <class Derived>
class PointBase {
public:
constexpr static const double PI = 3.14159265358979323846;
PointBase(float v0, float v1) : m_v0(v0), m_v1(v1) {
std::cout << "PointBase: " << this << std::endl;
}
float const &v0() const { return m_v0; }
float &v0() { return m_v0; }
float const &v1() const { return m_v1; }
float &v1() { return m_v1; }
float dist() const {
// Prevent the derived class from working if it doesn't define dist(),
// just like what a pure virtual function does.
static_assert(&PointBase<Derived>::dist != &Derived::dist,
"derived class must define dist()");
return static_cast<const Derived *>(this)->dist();
}
private:
Derived const &derived() const { return *static_cast<Derived const *>(this); }
float m_v0, m_v1;
private:
// safe guard for invalid inheritance
PointBase() {}
friend Derived;
}; /* end class PointBase */
template <typename Derived>
float use_dist(const PointBase<Derived> &pt) {
return pt.dist();
}
class CartesianPoint : public PointBase<CartesianPoint> {
public:
using PointBase<CartesianPoint>::PointBase;
float dist() const { return std::hypot(v0(), v1()); }
}; /* end class CartesianPoint */
class PolarPoint : public PointBase<PolarPoint> {
public:
using PointBase<PolarPoint>::PointBase;
float dist() const { return std::abs(v0()); }
}; /* end class PolarPoint */
class BadPoint : public PointBase<BadPoint> {
public:
using PointBase<BadPoint>::PointBase;
// Intentionally omit dist().
/*float dist() const
{
return 0.0;
}*/
}; /* end class BadPoint */
// intentionally iheritant from wrong base class
class BadBadPoint : public PointBase<BadPoint> {
public:
using PointBase<BadPoint>::PointBase;
};
int main(int, char **) {
std::cout << "sizeof(CartesianPoint) = " << sizeof(CartesianPoint)
<< std::endl;
std::cout << "sizeof(PolarPoint) = " << sizeof(PolarPoint) << std::endl;
CartesianPoint cpt(1, 1);
PolarPoint ppt(1, PolarPoint::PI / 4);
std::cout << "CartesianPoint(1,1)::dist() = " << use_dist(cpt) << std::endl;
std::cout << "PolarPoint(1, pi/4)::dist() = " << use_dist(ppt) << std::endl;
// This won't compile
// error: static_assert failed "derived class must define dist()"
// BadPoint bp;
// use_dist(bp);
// This won't compile
// since the default constructor is not accessible to BadBadPoint (due to
// incorrect usage of CRTP)
// BadBadPoint bbp;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment