Over the last years, type erasure or concept-based runtime polymorphism became quite popular, see e.g. the talks by Sean Parent Better Code: Runtime Polymorphism, Inheritance Is The Base Class of Evil or C++ Seasoning resp. the implementations by Adobe, Facebook, Boost.TypeErasure, Boost.te or dyno.
Here is an example from Sean Parent's talk:
class object_t {
struct concept_t {
virtual ~concept_t() = default;
virtual void draw_(ostream&, size_t) const = 0;
};
template <typename T>
struct model final : concept_t {
model(T x) : data_(move(x)) { }
void draw_(ostream& out, size_t position) const override
{ draw(data_, out, position); }
T data_;
};
shared_ptr<const concept_t> self_;
public:
template <typename T>
object_t(T x) : self_(make_shared<model<T>>(move(x))){ }
friend void draw(const object_t& x, ostream& out, size_t position)
{ x.self_->draw_(out, position); }
};
In the example above any class T
has to provide a function
void draw(T data, std::ostream out, size_t position);
which is simple to achieve for any T
since the return type is void
and the arguments signatures are all known a compile time. Same would be true, if the return type would by int, double, std::string etc.
Here is the actual question: How to do this for a custom return type RType
resp. argument type ArgType
?
class object_t {
struct concept_t {
virtual ~concept_t() = default;
virtual void draw_(ostream&, size_t) const = 0;
virtual RType foo (ArgType arg) const = 0; // new function
};
template <typename T>
struct model final : concept_t {
model(T x) : data_(move(x)) { }
void draw_(ostream& out, size_t position) const override
{ draw(data_, out, position); }
RType foo (ArgType arg) const override
{ return data_.foo(arg);}; // new function
T data_;
};
shared_ptr<const concept_t> self_;
public:
template <typename T>
object_t(T x) : self_(make_shared<model<T>>(move(x))){ }
friend void draw(const object_t& x, ostream& out, size_t position)
{ x.self_->draw_(out, position); }
RType foo (ArgType arg) const
{ return self_->foo(arg);}; // new function
};
(i) If RType
or ArgType
would be an explicit type, i.e. there exists a type class MyClass{};
we could define using RType = MyClass;
or using ArgType = MyClass
and everything is fine.
(ii) If RType
or ArgType
would be a concept-based polymorphic type such as object_t
it is also fine. But this would mean that the codebase would be full of interface classes such as object_t
. Don't get me wrong this would be a fair price to pay, but it will take some time to get used to it and structure a codebase to do so.
Now the problematic part: Can I make the example work if I have something like this using RType = typename T::RType
or using ArgType = typename T::ArgType
for a class T
???
I.e.
class T {
public:
using ArgType = /*...*/;
using RType = /*...*/;
/*...*/
};
Aucun commentaire:
Enregistrer un commentaire