Skip to content

Repository files navigation

TE

Boost LicenceVersionTry it online

Your C++17 one header only run-time polymorphism (type erasure) library with no dependencies, macros and limited boilerplate

Quick start

TE requires only one file. Get the latest header here!

#include<boost/te.hpp>// Requires C++17 (Tested: GCC-6+, Clang-4.0+, Apple:Clang-9.1+)namespacete= boost::te;

Erase it

// Define interface of something which is drawablestructDrawable {
voiddraw(std::ostream &out) const {
te::call([](autoconst &self, auto &out) { self.draw(out); }, *this, out);
}
};
// Define implementation which is drawable (No inheritance)structSquare {
voiddraw(std::ostream &out) const { out << "Square"; }
};
// Define other implementation which is drawable (No inheritance)structCircle {
voiddraw(std::ostream &out) const { out << "Circle"; }
};
// Define object which can hold drawable objectsvoiddraw(te::poly<Drawable> const &drawable) { drawable.draw(std::cout); }
intmain() {
// Call draw polymorphically (Value semantics / Small Buffer Optimization)draw(Circle{}); // prints Circledraw(Square{}); // prints Square
}

Alternatively, erase + declare it

structDrawable : te::poly<Drawable> {
using te::poly<Drawable>::poly;
voiddraw(std::ostream &out) const {
te::call([](autoconst &self, auto &out) { self.draw(out); }, *this, out);
}
};
voiddraw(Drawable const &drawable) { drawable.draw(std::cout); }
intmain() {
draw(Circle{}); // prints Circledraw(Square{}); // prints Square
}

Store it

intmain() {
std::vector<te::poly<Drawable>> drawables{};
drawables.push_back(Square{});
drawables.push_back(Circle{});
for (constauto &drawable : drawables) {
drawable.draw(std::cout); // prints Square Circle
}
}

Overload it

structAddable {
constexprautoadd(int i) { return te::call<int>(add_impl, *this, i); }
constexprautoadd(int a, int b) { return te::call<int>(add_impl, *this, a, b); }
private:staticconstexprauto add_impl = [](auto &self, auto... args) {
return self.add(args...);
};
};
classCalc {
public:constexprautoadd(int i) { return i; }
constexprautoadd(int a, int b) { return a + b; }
};
intmain() {
te::poly<Addable> addable{Calc{}};
assert(3 == addable.add(3));
assert(3 == addable.add(1, 2));
}

Override it

namespacev1 {
structDrawable {
voiddraw(std::ostream &out) const {
te::call([](autoconst &self, auto &&... args) { self.draw(args...); }, *this, out, "v1");
}
};
} // namespace v1namespacev2 {
structDrawable : v1::Drawable {
Drawable() { te::extends<v1::Drawable>(*this); }
// overridevoiddraw(std::ostream &out) const {
te::call([](autoconst &self, auto &&... args) { self.draw(args...); }, *this, out, "v2");
}
// extend/overloadvoiddraw(std::ostream& out, int minor) const {
te::call([](autoconst &self, auto &&... args) { self.draw(args...); },
*this, out, "v2." + std::to_string(minor));
}
};
} // namespace v2structSquare {
voiddraw(std::ostream &out, const std::string &v) const {
out << v << "::Square";
}
};
structCircle {
voiddraw(std::ostream &out, const std::string &v) const {
out << v << "::Circle";
}
};
template <classT, class... Ts>
voiddraw(te::poly<T> const &drawable, const Ts... args) {
drawable.draw(std::cout, args...);
}
intmain() {
draw<v1::Drawable>(Circle{}); // prints v1::Circle
draw<v1::Drawable>(Square{}); // prints v1::Square
draw<v2::Drawable>(Circle{}); // prints v2::Circle
draw<v2::Drawable>(Circle{}, 1); // prints v2.1::Circle
draw<v2::Drawable>(Square{}); // prints v2::Square
draw<v2::Drawable>(Square{}, 2); // prints v2.2::Square
}

Conceptify it (Requires C++20)

structDrawable {
voiddraw(std::ostream &out) const {
te::call([](autoconst &self, auto &out)
-> decltype(self.draw(out)) { self.draw(out); }, *this, out);
}
};
structSquare {
voiddraw(std::ostream &out) const { out << "Square"; }
};
structCircle {
// void draw(std::ostream &out) const { out << "Circle"; }
};
template<te::conceptify<Drawable> TDrawable>
voiddraw(TDrawable const &drawable) { drawable.draw(std::cout); }
intmain() {
{
te::var<Drawable> auto drawable = Square{};
drawable.draw(std::cout);// prints Square
}
{
// te::var<Drawable> auto drawable = Circle{}; // error: deduced initializer does not// drawable.draw(std::cout); // satisfy placeholder constraints (draw)
}
{
auto drawable = Square{};
draw(drawable); // prints Square
}
{
// auto drawable = Circle{}; // error: constraints not satisifed (draw)// draw(drawable);
}
}

Unify it

voiddraw(structCircleconst&, std::ostream&);
structDrawable {
voiddraw(std::ostream &out) const {
te::call(
[](autoconst &self, auto &out) {
ifconstexpr(std::experimental::is_detected<drawable_t, decltype(self), decltype(out)>{}) {
self.draw(out);
} else {
::draw(self, out);
}
}, *this, out
);
}
private:template<classT, class... Ts>
usingdrawable_t = decltype(std::declval<T>().draw(std::declval<Ts>()...));
};
structSquare {
voiddraw(std::ostream &out) const { out << "Member Square"; }
};
structCircle { };
voiddraw(Circle const&, std::ostream& out) {
out << "Non-member Circle";
}
voiddraw(te::poly<Drawable> const &drawable) { drawable.draw(std::cout); }
intmain() {
draw(Circle{}); // prints Non-member Circledraw(Square{}); // prints Member Square
}

Forward it

// headerstructDrawable {
voiddraw(std::ostream &out) const;
};
voiddraw(te::poly<Drawable> const &);
structSquare {
voiddraw(std::ostream &out) const { out << "Square"; }
};
structCircle {
voiddraw(std::ostream &out) const { out << "Circle"; }
};
// cppvoiddraw(te::poly<Drawable> const &drawable) { drawable.draw(std::cout); }
voidDrawable::draw(std::ostream& out) const {
te::call([](autoconst &self, auto &out) { self.draw(out); }, *this, out);
}
// uasageintmain() {
draw(Circle{}); // prints Circledraw(Square{}); // prints Square
}

Call it

template <class> structFunction;
template <classR, class... Ts> structFunction<R(Ts...)> {
constexprinlineautooperator()(Ts... args) {
return te::call<R>([](auto &self, Ts... args) { returnself(args...); }, *this, args...);
}
// Option 1 - Explicit requirements for class templates// template<class T>// auto requires__() -> decltype(&T::operator());
};
// Option 2 - Explicit template instantiation// template struct Function<int(int)>;intmain() {
// Option 1 or 2 is required on some compilers
te::poly<Function<int(int)>> f{[](int i) { return i; }};
assert(42 == f(42));
}

Customize it

intmain() {
te::poly<Drawable,
te::local_storage<16>, // te::dynamic_storage (default)
te::static_vtable // (default)
> drawable{Circle{}};
drawable.draw(std::cout); // prints Circle
}

Macro it?

#defineREQUIRES(R, name, ...) R { \
return ::te::call<R>( \
[](auto&& self, auto&&... args) { \
return self.name(std::forward<decltype(args)>(args)...); \
}, *this, ## __VA_ARGS__ \
); \
}
structAddable {
autoadd(int i) -> REQUIRES(int, add, i);
autoadd(int a, int b) -> REQUIRES(int, add, a, b);
};
classCalc {
public:autoadd(int i) { return i; }
autoadd(int a, int b) { return a + b; }
};
intmain() {
te::poly<Addable> addable{Calc{}};
assert(3 == addable.add(3));
assert(3 == addable.add(1, 2));
}

Inject it (DI)

classExample {
public:Example(Drawable const drawable, std::ostream& out)
: drawable{drawable}, out{out}
{ }
voiddraw() {
drawable.draw(out);
}
private:
Drawable const drawable;
std::ostream& out;
};
intmain() {
constauto injector = di::make_injector(
di::bind<Drawable>.to<Circle>(),
di::bind<std::ostream>.to(std::cout)
);
injector.create<Example>().draw(); // prints Circle
}

Mock it (GUnit)

"should mock drawable object"_test = [] {
GMock<Drawable> drawable;
EXPECT_CALL(drawable, draw(std::cout));
draw(drawable);
};

Similar libraries


DisclaimerTE is not an official Boost library.

About

C++17 Run-time Polymorphism (Type Erasure) library

Topics

Resources

Contributing

Stars

497 stars

Watchers

19 watching

Forks

Releases

Contributors

Languages