Instantly share code, notes, and snippets.
Created
November 1, 2023 23:50
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save srele96/d1e5bf385ef55a5e8debd40f1f4735dd to your computer and use it in GitHub Desktop.
C++, CRTP - Curiously Recurring Template Pattern + Attorney Pattern, Share Configuration
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
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