This is an insanely generic version of @Maxim's solution.
template<class B0, template<class...>class... Z>
struct TemplateFold {
using type=B0;
};
template<class B0, template<class...>class... Z>
using TemplateFold_t = typename TemplateFold<B0, Z...>::type;
template<class B0, template<class...>class Z0, template<class...>class... Z>
struct TemplateFold<B0, Z0, Z...>
{
using type=Z0< TemplateFold_t<B0, Z...> >;
};
struct ExposeTrivial {
protected:
~ExposeTrivial() {}
};
template<class D, class B0=ExposeTrivial, class...Bases>
struct Expose:B0, Bases... {
// is a template because D isn't a real type when this class is instantiated:
template<class T>
using MakeConcreteType = TemplateFold_t< T, std::conditional_t< std::is_same<B0,ExposeTrivial>{}, T, B0 >::template Implement, Bases::template Implement... >;
template<class...Args>
static std::unique_ptr<D> create( Args&&... args ) {
using ConcreteType = MakeConcreteType<D>;
return std::unique_ptr<D>( new ConcreteType( std::forward<Args>(args)... ) );
}
protected:
~Expose() {}
};
// expose one thing:
struct ExposeMemoryUsage:Expose<ExposeMemoryUsage> {
virtual std::size_t GetMemoryUsage() const noexcept = 0;
template<class B>
struct Implement:B {
using B::B;
std::size_t GetMemoryUsage() const noexcept override final {
return sizeof(*this);
}
};
protected:
~ExposeMemoryUsage() {}
};
// expose a different thing:
struct ExposeAlignment:Expose<ExposeAlignment>{
virtual std::size_t GetAlignment() const noexcept = 0;
template<class B>
struct Implement:B {
using B::B;
std::size_t GetAlignment() const noexcept final override {
return alignof(decltype(*this));
}
};
};
// Expose two things:
struct Bob : Expose<Bob, ExposeMemoryUsage, ExposeAlignment> {
int x;
Bob( int v ): x(v) {}
virtual ~Bob() {}
};
int main() {
std::unique_ptr<Bob> ptr = Bob::create(7);
std::cout << ptr->x << " size:" << ptr->GetMemoryUsage() << " align:" << ptr->GetAlignment() << "\n";
// Bob b; // does not compile
}
simply add more "knows the derived type" static helpers in Exposer
to increase functionality.
Live example.
How to use:
Create a Expose type. It should have a pure virtual member, and a template Implement class that (given a class that derives from the Expose type) implements that pure virtual member.
It should inherit from Expose<OwnType>
(CRTP) to write the static ::create
method for you.
If you want to inherit from additional Expose
types (ie, compose two independent Expose interfaces that need to know the concrete type), instead inherit from Expose< YourType, OtherExposeType, AnotherExposeType >
. Don't independently inherit from OtherExposeType
or AnotherExposeType
.
If you do this your Implement
template won't be picked up.
I could improve this so that we detect Implement
templates in both you and your bases, but that is more metaprogramming than I'm up for right now.
sizeof
on the actual object than having a function for it. – Zygodactylsizeof(*this)
is not a good measure for the (total) memory usage. Try egsizeof(some_string)
orsizeof(some_vector)
– Tychonn;
after struct definition. – Pavissizeof(B)
to get the size of aB
instance? – TychonnB
whereas with your answer, using B would be wrong (fortunately, it is pure as A)), both answer are valid :-).decltype(*this)
is not part of the duplicated question (which indeed would make it a wrong duplicate), but it is not part of your answer either. – Catty