c++11 - multiple inheritance with concept-based polymorphism c++ -
using concept-based polymorphism focuses on using value semantics , type erasure implement type polymorphism, how 1 implement multiple inheritance?
an example of interface follows. defines type, serializable, require functions serialize , size appropriate signature.
#include <iostream> #include <memory> using namespace std; class serializable { public: template <typename t> serializable(t x) : self_(new model<t>(move(x))) { } friend size_t serialize(const serializable& x, uint8_t* buffer, size_t buffersize) { x.self_->serialize_(buffer, buffersize); } friend size_t size(const serializable& x) { x.self_->size_(); } private: struct concept { virtual ~concept() = default; virtual size_t serialize_(uint8_t*, size_t buffersize) const = 0; virtual size_t size_() const = 0; }; template <typename t> struct model : concept { model(t x) : data_(move(x)) { } size_t serialize_(uint8_t* buffer, size_t buffersize) const { serialize(data_, buffer, buffersize); } size_t size_() const { size(data_); } t data_; }; shared_ptr<const concept> self_; };
a class implements interface shown below. instead of directly inheriting pure virtual functions, uses same function names. myclassb later used , identical implementation, different name.
class myclassa { public: myclassa(const string& name) : name_(name) { } friend size_t serialize(const myclassa& x, uint8_t* buffer, size_t buffersize); friend size_t size(const myclassa& x); private: const string name_; }; size_t serialize(const myclassa& x, uint8_t* buffer, size_t buffersize) { size_t bytestowrite = size(x); if(bytestowrite <= buffersize) { memcpy(buffer, x.name_.c_str(), bytestowrite); } else { bytestowrite = 0; } return bytestowrite; } size_t size(const myclassa& x) { x.name_.length(); }
in addition, same signature created vector.
size_t size(const vector<serializable>& x) { size_t totalsize = 0; (auto& e : x) { totalsize += size(e); } return totalsize; } size_t serialize(const vector<serializable>& x, uint8_t* buffer, size_t buffersize) { size_t bytestowrite = size(x); size_t offset = 0; if(bytestowrite <= buffersize) { (auto& e : x) { offset += serialize(e, buffer + offset, buffersize - offset); } } else { bytestowrite = 0; } return bytestowrite; }
to use type, myclassa, myclassb , vector, client-side code below creates objects, adds them vector , prints them.
int main() { vector<serializable> channel; uint8_t buffer[30] = {}; myclassa myclassa("hello world!"); serialize(myclassa, buffer, sizeof buffer); cout << buffer << endl; cout << "-------------------" << endl; channel.emplace_back(myclassa(" apples")); channel.emplace_back(myclassb(" oranges")); serialize(channel, buffer, sizeof buffer); cout << buffer << endl; cout << "-------------------" << endl; cout << size(channel) << endl; cout << "-------------------" << endl; vector<serializable> channel2; channel2.emplace_back(channel); channel2.emplace_back(channel); serialize(channel2, buffer, sizeof buffer); cout << buffer << endl; }
the code has been prepared in following repository. build run scons in root directory: https://github.com/moritz89/type-erasure-test?files=1
back original question, possible create additional interfaces, e.g., reporterror or client, define functions have implemented , can unified create multiple inheritance structures?
you can this, need change approach bit don't end needlessly duplicating. first, need simple empty_model
class:
template <typename t, typename ... <class> models> struct empty_model : models<empty_model> { model(t x) : data_(move(x)) { } t data_; };
this has whatever code necessary build proxy t
. next, have turn our per-concept models things only handle forwarding interface, not ownership, via crtp:
// still nested in serializable, or whatever concept template <typename d> struct model : concept { size_t serialize_(uint8_t* buffer, size_t buffersize) const { serialize(static_cast<const d&>(*this).data_, buffer, buffersize); } size_t size_() const { size(static_cast<const d&>(*this).data_); } };
so, each concept, writing nested model
class bit different, far amount of boiler plate not larger. use typedefs create actual models want:
template <class t> using serializable_model = empty_model<t, serializable::model>; template <class t> using my_model = empty_model<t, serializable::model, reporterror::model>;
the last thing need change concept have constructor accepts actual shared_ptr
:
serializable(shared_ptr<const concept> x) : self_(move(x)) {}
so in principle write concrete class, foo
, , like:
auto f = make_shared<my_model<foo>>(foo{});
remember (yeah, it's getting hard...) my_model
inherit nested model classes (suitably templated), inherit base concepts. means my_model<foo>
grandson of serializable::concept
, f
implicitly convert can this:
serializable s(f);
all said: while sean parents talks brilliant (yes easy recognize), lot of work, complexity , boilerplate. in experience, 99% of time in real code, better writing off interface, , writing concrete type directly inherits them. i'd make sure specific problem benefits before going down rabbit hole.
Comments
Post a Comment