Skip to content

FeasibilityTest

Matthias Melcher edited this page Dec 31, 2025 · 5 revisions

Of Shims and Pimpls

I did a first test implementation of fltk3 with an FLTK1 compatibility layer. To do this, I used the same approach as adding support for a different programming language. The fltk3::Widget base class has a new member, shim_, and the shim class Fl_Widget has only one member, a pointer back to the fltk3::Widget that implements widget functionality, impl_.

Shims

A shim is a class that translates FLTK1 class method calls into fltk3 calls. As long as fltk3 and FLTK1 don't diverge too much, all shims can be implemented in a header-only library with inline functions. A good compiler will optimize most of the shim away, keeping emulated FLTK1 still fast and light.

Pimpl

Shim classes have only one member, a "pointer to implementation", commonly known as pimpl.

Virtual Methods

FLTK1 user implementations can still override methods like draw() and handle(), and still call the original method without any changes to the source code. For that, the fltk3 implementation provides abstract interface classes that the shims will use to implement overridable virtual methods.

Using interface classes, fltk3 does not need to know anything about FLTK1 and how shims are implemented. This is great for implementing all kinds of shims, emulating different APIs or interfacing to different programming languages. Shims can translate all aspects of fltk3 without needing changes in fltk3 core code.

A First Implementation Attempt

The fltk3 branch is a first stab at implementing shims. I renamed the FL directory to fltk3 and made all adjustments needed to compile test/button. I want to test my implementation by mixing fltk3 and FLTK1 buttons in the "button" test app.

I renamed Fl_Widget and Fl_Button to fltk3::Widget and fltk3::Button and created the shims.

In [fltk3/Widget.H](https://github.com/MatthiasWM/fltk/blob/fltk3/fltk3/Widget.H) I also added IWidgetShim, the shim_ pointer, and a constructor that sets the shim.

fltk3/Button.H gets a very similar treatment. I provided IButtonShim for testing an additional override ale virtual method, but it's not technically needed here.

Overridable virtual functions need one extra hop in the fltk3 implementation to allow shims to override them. So fltk3::Button::draw() is now divided into dispatch and implementation.

The FLTK1 shims use multiple inheritance (it's perfectly fine to do with abstract classes) to translate from 1 to 3. The F1 constructor also creates the f3 widget and links them through shim_ and impl_. The destructor also destroy the implementation and vice versa (or not, if this were an interface to another language).

The only other special case here are again overridable virtual methods. They need to call the implementor method instead of the virtual method.

Everything else is just a ton of casting that will be optimized away by the compiler.

Why are we doing this?

If the text above sounds complicated at first, the implementation really isn't once we got the pattern down. The FL shim can be written in a week or two. The bigger work (but also the fun part) is going through the fltk3 API and finally fix and modernize everything we ever wanted to fix about FLTK1 without sacrificing any back compatibility.

Have a last look at test/button. The code mixes fltk3 (b2, b3) and FLTK1 (b1) buttons easily. We could derive a new class from Fl_Button or fltk3::Button, either with a new draw() method, and everything would still work as expected.

Clone this wiki locally