$ mkdir -p build
$ cmake -B build -S .
$ cmake --build build
Last active
March 16, 2022 09:33
-
-
Save dboyliao/95f827d584df1c8b66cb8d0fe55b6725 to your computer and use it in GitHub Desktop.
Simple C++ Example for CRTP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
build | |
lib | |
bin | |
.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <algorithm> | |
#include <cmath> | |
#include "public.hpp" | |
float CartesianPoint::dist() const { return std::hypot(v0(), v1()); } | |
float PolarPoint::dist() const { return std::abs(v0()); } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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