Compile-Time Polymorphism in C++: Implementation with folly::poly and dyno
The article explains how compile‑time polymorphism in modern C++—using libraries like folly::poly and dyno—can replace traditional virtual‑table based runtime polymorphism by employing small‑object optimization, compile‑time generated VTables, and type‑erased wrappers, delivering near‑virtual performance without inheritance or heap allocation.
This article explores compile-time polymorphism in C++ and compares it with runtime reflection. It discusses how recent C++ compiler features enable mechanisms similar to Go interfaces and Rust traits, and introduces two open‑source implementations: dyno and folly::poly.
1. Runtime Polymorphism Review
The article begins by revisiting runtime polymorphism, highlighting its performance overhead due to virtual tables and pointer indirection, as well as its intrusive nature (requiring inheritance and heap allocation).
2. Introduction to dyno and folly::poly
Both libraries provide compile‑time polymorphism. dyno relies on boost::hana and other third‑party libraries, while folly::poly uses only standard C++17 features. The focus of the article is on folly::poly’s implementation.
3. Simple Example
A basic example of a classic runtime polymorphic hierarchy is shown:
struct Vehicle {
virtual void accelerate() = 0;
virtual ~Vehicle() {}
};
struct Car : public Vehicle {
void accelerate() override;
};
struct Truck : public Vehicle {
void accelerate() override;
};The article then presents a compile‑time version using folly::poly.
4. folly::poly Example
#include <folly/Poly.h>
struct IVehicle {
template <class Base>
struct Interface : Base {
void accelerate() const { folly::poly_call<0>(*this); }
};
template <class T>
using Members = folly::PolyMembers<&T::accelerate>;
};
using vehicle = folly::Poly<IVehicle>;
struct Car {
void accelerate() const { std::cout << "Car accelerate!" << std::endl; }
};
struct Trunk {
void accelerate() const { std::cout << "Trunk accelerate!" << std::endl; }
};
void accel_func(vehicle const& v) { v.accelerate(); }
int main() {
accel_func(Car{});
accel_func(Trunk{});
return 0;
}This code demonstrates non‑intrusive polymorphism: concrete types need not inherit from a common base.
5. Implementation Details
The core of folly::poly consists of two policies:
Storage policy – decides whether an object can be stored in‑situ (small‑object optimization) or on the heap. The inSitu<U>() check ensures the object fits into a fixed-size buffer and is nothrow‑move‑constructible.
VTable policy – creates a compile‑time generated virtual table that erases member‑function types. The VTable is built using vtableFor<I, T>(), which returns a singleton instance via StaticConst.
Key structures include:
struct Data {
union { void* pobj_ = nullptr; std::aligned_storage_t<sizeof(double[2])> buff_; };
};
template <class I, class T>
struct VTableFor : VTable<I> { constexpr VTableFor() noexcept : VTable<I>{Type<T>{}} {} };The VTable stores function pointers generated by ThunkFn, which wrap member functions into a uniform signature like void(*)(const folly::detail::Data&). This eliminates the need for runtime type information while keeping call overhead minimal.
6. Performance Considerations
Assembly generated for accel_func(Car{}) shows that after inlining, the call is comparable to a virtual dispatch, with the overhead of loading the function pointer from the VTable.
7. Summary
folly::poly achieves non‑intrusive, compile‑time polymorphism by combining small‑object optimization, compile‑time VTable generation, and type‑erased function wrappers. It offers performance close to virtual dispatch without the drawbacks of runtime inheritance.
The article concludes with suggestions for future work, such as leveraging upcoming C++ reflection features or code generation to further simplify interface definitions.
Tencent Cloud Developer
Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
