Skip to content

Instantly share code, notes, and snippets.

@srele96
Created November 1, 2023 23:50
Show Gist options
  • Save srele96/d1e5bf385ef55a5e8debd40f1f4735dd to your computer and use it in GitHub Desktop.
Save srele96/d1e5bf385ef55a5e8debd40f1f4735dd to your computer and use it in GitHub Desktop.
C++, CRTP - Curiously Recurring Template Pattern + Attorney Pattern, Share Configuration
namespace share_config {
/**
* Do not try to understand the WHY this is the way it is. It just is.
*
* I am using CRTP and Attorney pattern because I want to use them lol.
*/
struct config {
std::function<void(const std::string&, std::ostream&)> callback_one;
std::function<void(const std::string&, std::ostream&)> callback_two;
};
// https://stackoverflow.com/questions/56314295/crtp-can-i-make-a-private-method
// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Friendship_and_the_Attorney-Client
template <typename T>
class attorney;
template <typename T>
class device {
private:
config m_create_config() const {
return config{[](const std::string& label, std::ostream& ostream) {
ostream << label << "\n";
},
[](const std::string& label, std::ostream& ostream) {
ostream << label << "\n";
}};
}
public:
void configure() {
attorney<T>::m_configure(*static_cast<T*>(this), m_create_config());
}
};
struct ostreams {
std::reference_wrapper<std::ostream> ostream_one;
std::reference_wrapper<std::ostream> ostream_two;
};
class smart_light : public device<smart_light> {
private:
// Shut up linter that method can be made static. I hope it makes sense now.
ostreams m_ostreams;
friend class attorney<smart_light>;
void m_configure(const config& config) const {
// All this work just to print something to the console?
// Are you alright?
// Sure you got all sheeps in the herd?
config.callback_one("smart-light, one", m_ostreams.ostream_one.get());
config.callback_two("smart-light, two", m_ostreams.ostream_two.get());
}
public:
explicit smart_light(ostreams p_ostreams) : m_ostreams{p_ostreams} {}
};
class smart_heater : public device<smart_heater> {
private:
friend class attorney<smart_heater>;
// Shut up linter that method can be made static. I hope it makes sense now.
ostreams m_ostreams;
void m_configure(const config& config) const {
config.callback_one("smart-heater, one", m_ostreams.ostream_one.get());
config.callback_two("smart-heater, two", m_ostreams.ostream_two.get());
}
public:
explicit smart_heater(ostreams p_ostreams) : m_ostreams{p_ostreams} {}
};
class smart_door : public device<smart_door> {
private:
friend class attorney<smart_door>;
// Shut up linter that method can be made static. I hope it makes sense now.
ostreams m_ostreams;
void m_configure(const config& config) const {
config.callback_one("smart-door, one", m_ostreams.ostream_one.get());
config.callback_two("smart-door, two", m_ostreams.ostream_two.get());
}
public:
explicit smart_door(ostreams p_ostreams) : m_ostreams{p_ostreams} {}
};
// Hide private members of concrete devices from the base device. Alternatively
// don't use attorney technique and use two way friendship between device and
// concrete device.
template <typename Derived>
class attorney {
private:
friend class device<Derived>;
static void m_configure(const Derived& derived, const config& config) {
derived.m_configure(config);
}
};
void run() {
// Reference wrapper is an interesting concept. So much effort for type
// safety.
const ostreams light_ostreams{std::ref(std::cout), std::ref(std::cout)};
const ostreams heater_ostreams{std::ref(std::cout), std::ref(std::cout)};
const ostreams door_ostreams{std::ref(std::cout), std::ref(std::cout)};
// Trivially copyable. It is worth exploring why it's trivially copyable and
// how did they avoid using raw references.
smart_light light{light_ostreams};
smart_heater heater{heater_ostreams};
smart_door door{door_ostreams};
// We could pretend to be smart, erase the type, and pretend that each object
// has configure method and invoke it.
//
// Do it as a potential challenge.
light.configure();
heater.configure();
door.configure();
// An idea. Try to plug in inner dispatcher system between devices.
}
} // namespace share_config
int main() {
share_config::run();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment