Skip to content

Instantly share code, notes, and snippets.

@dboyliao
Last active March 16, 2022 09:33
Show Gist options
  • Save dboyliao/95f827d584df1c8b66cb8d0fe55b6725 to your computer and use it in GitHub Desktop.
Save dboyliao/95f827d584df1c8b66cb8d0fe55b6725 to your computer and use it in GitHub Desktop.
Simple C++ Example for CRTP
build
lib
bin
.vscode
cmake_minimum_required(VERSION 3.8)
project(crtp_example LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_VERBOSE_MAKEFILE ON)
add_library(public_lib SHARED private.cpp)
target_include_directories(public_lib
PUBLIC ${CMAKE_SOURCE_DIR}
)
set_target_properties(public_lib
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib
)
add_executable(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/crtp.cpp)
target_link_libraries(${PROJECT_NAME} public_lib)
set_target_properties(${PROJECT_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin
)
#include <cstdint>
#include <utility>
#include "public.hpp"
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;
}
#include <algorithm>
#include <cmath>
#include "public.hpp"
float CartesianPoint::dist() const { return std::hypot(v0(), v1()); }
float PolarPoint::dist() const { return std::abs(v0()); }
#include <iostream>
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 {
static_assert(&Derived::dist != &PointBase::dist,
"derived class must overwrite dist");
auto &obj = derived();
return obj.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;
private:
float dist() const;
friend class PointBase<CartesianPoint>;
};
class PolarPoint : public PointBase<PolarPoint> {
public:
using PointBase<PolarPoint>::PointBase;
private:
float dist() const;
friend class PointBase<PolarPoint>;
};
class BadPoint : public PointBase<BadPoint> {
public:
using PointBase<BadPoint>::PointBase;
private:
friend class PointBase<BadPoint>;
};
class BadBadPoint : public PointBase<BadPoint> {
public:
using PointBase<BadPoint>::PointBase;
private:
friend class PointBase<BadPoint>;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment