dynamic polymorphism without inheritance
Type-erased polymorphic interfaces
Note: This project is based on the Boost.FunctionTypes interface example by Tobias Schwinger.
An interface is a collection of member function prototypes that may be implemented by classes. Objects of classes that implement the interface can then be assigned to an interface variable through which the interface's functions can be called.
Interfaces are a prominent feature in many object-oriented programming languages, such as Java. Historically, an "interface" in C++ is modeled by a pure abstract base class. However, the inheritance-based approach to interfaces has the following drawbacks:
Fortunately, it is possible to mitigate some or all of these drawbacks by using an alternative approach based on type erasure. Eraserface is an implementation of this approach using template metaprogramming and preprocessor metaprogramming techniques.
Eraserface is contained to a single header file:
#include <eraserface/eraserface.hpp>
The DEFINE_ERASERFACE
macro generates a type-erased interface type which can be used to apply an interface to an object, without altering class definitions. This allows objects to be interfaced polymorphically whose class definitions are not accessible to the programmer. In addition, the presence of implementations are checked and enforced at compile time, so there is no safety disadvantage to using this technique. Eraserface even accounts for member data, such as some_data
below:
DEFINE_ERASERFACE( my_interface,
(( a_func, void(int) const ))
(( another_func, int() ))
(( some_data, int ))
);
This macro will generate an interface that roughly corresponds to the following abstract base class:
struct my_interface {
virtual void a_func(int) const = 0;
virtual int another_func() = 0;
//(imagine a virtual data member, accessible with a function call)
virtual int some_data;
};
The difference is that an Eraserface interface is applied "inline", instead of at a class definition :
my_interface i = some_object;
First and foremost, Eraserface is a fun metaprogramming exercise. A handful of test cases exist in this repository, which one should review (and perhaps expand) before deciding to use Eraserface in their own project.
Eraserface requires access to member function pointers through the target object type. This brings 4 important considerations:
using
declarations in the derived class.std
namespace may not be used, since taking the address of a member function of a class in this namespace is undefined behavior.DEFINE_ERASERFACE
macro.The Eraserface macro adds two names to the current scope. The first macro parameter (e.g. my_interface
in the previous example) is expanded to a struct. The first macro parameter is also appended with _detail_
, which is a class in the current scope (e.g. my_interface_detail_
in the previous example).
Dependencies must be available in the include path.
Eraserface worked at some point on GCC 4.9.3+ and Clang 3.8+, and maybe it still does. I need to set up continous integration tests. MSVC is not supported, largely (if not entirely) due to name-lookup compiler bugs.
Distributed under the Boost Software License, Version 1.0.