1. Introduction
We introduce a library template class representing a non-owning pointer-like wrapper to an object that is convertible to a pointer to each interface. Consider the following code sample:
#include <memory>// chimeric_ptr struct Editor { virtual void Edit ( void ) = 0 ; }; struct Processor { virtual void Process ( void ) = 0 ; }; void EditAndProcess ( std :: chimeric_ptr < Editor , Processor > const p ) { p -> Edit (); p -> Process (); } struct Emulator : Editor , Processor { void Edit ( void ) override {} void Process ( void ) override {} }; struct Simulator : Editor , Processor { void Edit ( void ) override {} void Process ( void ) override {} }; int main ( void ) { Emulator e ; EditAndProcess ( & e ); Simulator s ; EditAndProcess ( & s ); }
GodBolt for above snippet: https://godbolt.org/z/jqEbqY7o5
In the above code snippet inside the function , the member named is looked up in the interface list from left to right, selecting the first interface where lookup succeeds, and the expression is treated as if written using a cast to that interface pointer, as though the function had originally been written as:
void EditAndProcess ( std :: chimeric_ptr < Editor , Processor > const p ) { static_cast < Editor *> ( p ) -> Edit (); static_cast < Processor *> ( p ) -> Process (); }
GodBolt for above snippet: https://godbolt.org/z/z17f9vMTa
The convenience here is that the programmer does not need to indicate which interface contains which member, meaning that the code that makes use of the interfaces does not need to be revised when the interfaces are edited.
2. Motivation
In graphical user interface frameworks and also in other large object-orientated codebases, it is common for concrete types to model multiple orthogonal abstract interfaces.
Consider a function that needs to act on an object through two different interfaces. We can define a single base class that subsumes both interfaces, for example:
struct Editor { virtual void Edit ( void ) = 0 ; }; struct Processor { virtual void Process ( void ) = 0 ; }; struct EditorCumProcessor : Editor , Processor {}; void EditAndProcess ( EditorCumProcessor * const p ) { p -> Edit (); p -> Process (); } struct Emulator : EditorCumProcessor { void Edit ( void ) override {} void Process ( void ) override {} }; struct Simulator : EditorCumProcessor { void Edit ( void ) override {} void Process ( void ) override {} };
GodBolt for above snippet: https://godbolt.org/z/c9KrMq43n
This is okay when dealing with just two interfaces, but it quickly balloons when dealing with multiple base classes, for example consider the following concrete class taken from the software development kit for a production microscope:
struct Vetrayor : virtual IMicroscope , virtual IUtility , virtual IDrift , virtual IIllumination , virtual IStage , virtual IEscape , virtual IObjective , virtual IDiagnostics , virtual IIris , virtual IPower , virtual IAsyncControl , virtual ICommsHandler { . . . };
If we were to define a class for each combination of two interfaces, we would have 66 new classes as follows:
struct IAsyncControl_CommsHandler : virtual IAsyncControl , virtual ICommsHandler {}; struct IAsyncControl_Diagnostics : virtual IAsyncControl , virtual IDiagnostics {}; struct IAsyncControl_Drift : virtual IAsyncControl , virtual IDrift {}; struct IAsyncControl_Escape : virtual IAsyncControl , virtual IEscape {}; struct IAsyncControl_Illlumination : virtual IAsyncControl , virtual IIllumination {}; struct IAsyncControl_Iris : virtual IAsyncControl , virtual IIris {}; struct IAsyncControl_Microscope : virtual IAsyncControl , virtual IMicroscope {}; struct IAsyncControl_Objective : virtual IAsyncControl , virtual IObjective {}; struct IAsyncControl_Power : virtual IAsyncControl , virtual IPower {}; struct IAsyncControl_Stage : virtual IAsyncControl , virtual IStage {}; struct IAsyncControl_Utility : virtual IAsyncControl , virtual IUtility {}; struct ICommsHandler_Diagnostics : virtual ICommsHandler , virtual IDiagnostics {}; struct ICommsHandler_Drift : virtual ICommsHandler , virtual IDrift {}; struct ICommsHandler_Escape : virtual ICommsHandler , virtual IEscape {}; struct ICommsHandler_Illlumination : virtual ICommsHandler , virtual IIllumination {}; struct ICommsHandler_Iris : virtual ICommsHandler , virtual IIris {}; struct ICommsHandler_Microscope : virtual ICommsHandler , virtual IMicroscope {}; struct ICommsHandler_Objective : virtual ICommsHandler , virtual IObjective {}; struct ICommsHandler_Power : virtual ICommsHandler , virtual IPower {}; struct ICommsHandler_Stage : virtual ICommsHandler , virtual IStage {}; struct ICommsHandler_Utility : virtual ICommsHandler , virtual IUtility {}; struct IDiagnostics_Drift : virtual IDiagnostics , virtual IDrift {}; struct IDiagnostics_Escape : virtual IDiagnostics , virtual IEscape {}; struct IDiagnostics_Illlumination : virtual IDiagnostics , virtual IIllumination {}; struct IDiagnostics_Iris : virtual IDiagnostics , virtual IIris {}; struct IDiagnostics_Microscope : virtual IDiagnostics , virtual IMicroscope {}; struct IDiagnostics_Objective : virtual IDiagnostics , virtual IObjective {}; struct IDiagnostics_Power : virtual IDiagnostics , virtual IPower {}; struct IDiagnostics_Stage : virtual IDiagnostics , virtual IStage {}; struct IDiagnostics_Utility : virtual IDiagnostics , virtual IUtility {}; struct IDrift_Escape : virtual IDrift , virtual IEscape {}; struct IDrift_Illlumination : virtual IDrift , virtual IIllumination {}; struct IDrift_Iris : virtual IDrift , virtual IIris {}; struct IDrift_Microscope : virtual IDrift , virtual IMicroscope {}; struct IDrift_Objective : virtual IDrift , virtual IObjective {}; struct IDrift_Power : virtual IDrift , virtual IPower {}; struct IDrift_Stage : virtual IDrift , virtual IStage {}; struct IDrift_Utility : virtual IDrift , virtual IUtility {}; struct IEscape_Illlumination : virtual IEscape , virtual IIllumination {}; struct IEscape_Iris : virtual IEscape , virtual IIris {}; struct IEscape_Microscope : virtual IEscape , virtual IMicroscope {}; struct IEscape_Objective : virtual IEscape , virtual IObjective {}; struct IEscape_Power : virtual IEscape , virtual IPower {}; struct IEscape_Stage : virtual IEscape , virtual IStage {}; struct IEscape_Utility : virtual IEscape , virtual IUtility {}; struct IIllumination_Iris : virtual IIllumination , virtual IIris {}; struct IIllumination_Microscope : virtual IIllumination , virtual IMicroscope {}; struct IIllumination_Objective : virtual IIllumination , virtual IObjective {}; struct IIllumination_Power : virtual IIllumination , virtual IPower {}; struct IIllumination_Stage : virtual IIllumination , virtual IStage {}; struct IIllumination_Utility : virtual IIllumination , virtual IUtility {}; struct IIris_Microscope : virtual IIris , virtual IMicroscope {}; struct IIris_Objective : virtual IIris , virtual IObjective {}; struct IIris_Power : virtual IIris , virtual IPower {}; struct IIris_Stage : virtual IIris , virtual IStage {}; struct IIris_Utility : virtual IIris , virtual IUtility {}; struct IMicroscope_Objective : virtual IMicroscope , virtual IObjective {}; struct IMicroscope_Power : virtual IMicroscope , virtual IPower {}; struct IMicroscope_Stage : virtual IMicroscope , virtual IStage {}; struct IMicroscope_Utility : virtual IMicroscope , virtual IUtility {}; struct IObjective_Power : virtual IObjective , virtual IPower {}; struct IObjective_Stage : virtual IObjective , virtual IStage {}; struct IObjective_Utility : virtual IObjective , virtual IUtility {}; struct IPower_Stage : virtual IPower , virtual IStage {}; struct IPower_Utility : virtual IPower , virtual IUtility {}; struct IStage_Utility : virtual IStage , virtual IUtility {};
Furthermore, if we were to define a class for each combination of three interfaces, we would have another 220 new classes:
struct IAsyncControl_ICommsHandler_IDiagnostics : virtual IAsyncControl , virtual ICommsHandler , virtual IDiagnostics {}; struct IAsyncControl_ICommsHandler_IDrift : virtual IAsyncControl , virtual ICommsHandler , virtual IDrift {}; struct IAsyncControl_ICommsHandler_IEscape : virtual IAsyncControl , virtual ICommsHandler , virtual IEscape {}; struct IAsyncControl_ICommsHandler_IIllumination : virtual IAsyncControl , virtual ICommsHandler , virtual IIllumination {}; struct IAsyncControl_ICommsHandler_IIris : virtual IAsyncControl , virtual ICommsHandler , virtual IIris {}; struct IAsyncControl_ICommsHandler_IMicroscope : virtual IAsyncControl , virtual ICommsHandler , virtual IMicroscope {}; struct IAsyncControl_ICommsHandler_IObjective : virtual IAsyncControl , virtual ICommsHandler , virtual IObjective {}; struct IAsyncControl_ICommsHandler_IPower : virtual IAsyncControl , virtual ICommsHandler , virtual IPower {}; struct IAsyncControl_ICommsHandler_IStage : virtual IAsyncControl , virtual ICommsHandler , virtual IStage {}; struct IAsyncControl_ICommsHandler_IUtility : virtual IAsyncControl , virtual ICommsHandler , virtual IUtility {}; struct IAsyncControl_IDiagnostics_IDrift : virtual IAsyncControl , virtual IDiagnostics , virtual IDrift {}; struct IAsyncControl_IDiagnostics_IEscape : virtual IAsyncControl , virtual IDiagnostics , virtual IEscape {}; struct IAsyncControl_IDiagnostics_IIllumination : virtual IAsyncControl , virtual IDiagnostics , virtual IIllumination {}; struct IAsyncControl_IDiagnostics_IIris : virtual IAsyncControl , virtual IDiagnostics , virtual IIris {}; struct IAsyncControl_IDiagnostics_IMicroscope : virtual IAsyncControl , virtual IDiagnostics , virtual IMicroscope {}; struct IAsyncControl_IDiagnostics_IObjective : virtual IAsyncControl , virtual IDiagnostics , virtual IObjective {}; struct IAsyncControl_IDiagnostics_IPower : virtual IAsyncControl , virtual IDiagnostics , virtual IPower {}; struct IAsyncControl_IDiagnostics_IStage : virtual IAsyncControl , virtual IDiagnostics , virtual IStage {}; struct IAsyncControl_IDiagnostics_IUtility : virtual IAsyncControl , virtual IDiagnostics , virtual IUtility {}; struct IAsyncControl_IDrift_IEscape : virtual IAsyncControl , virtual IDrift , virtual IEscape {}; struct IAsyncControl_IDrift_IIllumination : virtual IAsyncControl , virtual IDrift , virtual IIllumination {}; struct IAsyncControl_IDrift_IIris : virtual IAsyncControl , virtual IDrift , virtual IIris {}; struct IAsyncControl_IDrift_IMicroscope : virtual IAsyncControl , virtual IDrift , virtual IMicroscope {}; struct IAsyncControl_IDrift_IObjective : virtual IAsyncControl , virtual IDrift , virtual IObjective {}; struct IAsyncControl_IDrift_IPower : virtual IAsyncControl , virtual IDrift , virtual IPower {}; struct IAsyncControl_IDrift_IStage : virtual IAsyncControl , virtual IDrift , virtual IStage {}; struct IAsyncControl_IDrift_IUtility : virtual IAsyncControl , virtual IDrift , virtual IUtility {}; struct IAsyncControl_IEscape_IIllumination : virtual IAsyncControl , virtual IEscape , virtual IIllumination {}; struct IAsyncControl_IEscape_IIris : virtual IAsyncControl , virtual IEscape , virtual IIris {}; struct IAsyncControl_IEscape_IMicroscope : virtual IAsyncControl , virtual IEscape , virtual IMicroscope {}; struct IAsyncControl_IEscape_IObjective : virtual IAsyncControl , virtual IEscape , virtual IObjective {}; struct IAsyncControl_IEscape_IPower : virtual IAsyncControl , virtual IEscape , virtual IPower {}; struct IAsyncControl_IEscape_IStage : virtual IAsyncControl , virtual IEscape , virtual IStage {}; struct IAsyncControl_IEscape_IUtility : virtual IAsyncControl , virtual IEscape , virtual IUtility {}; struct IAsyncControl_IIllumination_IIris : virtual IAsyncControl , virtual IIllumination , virtual IIris {}; struct IAsyncControl_IIllumination_IMicroscope : virtual IAsyncControl , virtual IIllumination , virtual IMicroscope {}; struct IAsyncControl_IIllumination_IObjective : virtual IAsyncControl , virtual IIllumination , virtual IObjective {}; struct IAsyncControl_IIllumination_IPower : virtual IAsyncControl , virtual IIllumination , virtual IPower {}; struct IAsyncControl_IIllumination_IStage : virtual IAsyncControl , virtual IIllumination , virtual IStage {}; struct IAsyncControl_IIllumination_IUtility : virtual IAsyncControl , virtual IIllumination , virtual IUtility {}; struct IAsyncControl_IIris_IMicroscope : virtual IAsyncControl , virtual IIris , virtual IMicroscope {}; struct IAsyncControl_IIris_IObjective : virtual IAsyncControl , virtual IIris , virtual IObjective {}; struct IAsyncControl_IIris_IPower : virtual IAsyncControl , virtual IIris , virtual IPower {}; struct IAsyncControl_IIris_IStage : virtual IAsyncControl , virtual IIris , virtual IStage {}; struct IAsyncControl_IIris_IUtility : virtual IAsyncControl , virtual IIris , virtual IUtility {}; struct IAsyncControl_IMicroscope_IObjective : virtual IAsyncControl , virtual IMicroscope , virtual IObjective {}; struct IAsyncControl_IMicroscope_IPower : virtual IAsyncControl , virtual IMicroscope , virtual IPower {}; struct IAsyncControl_IMicroscope_IStage : virtual IAsyncControl , virtual IMicroscope , virtual IStage {}; struct IAsyncControl_IMicroscope_IUtility : virtual IAsyncControl , virtual IMicroscope , virtual IUtility {}; struct IAsyncControl_IObjective_IPower : virtual IAsyncControl , virtual IObjective , virtual IPower {}; struct IAsyncControl_IObjective_IStage : virtual IAsyncControl , virtual IObjective , virtual IStage {}; struct IAsyncControl_IObjective_IUtility : virtual IAsyncControl , virtual IObjective , virtual IUtility {}; struct IAsyncControl_IPower_IStage : virtual IAsyncControl , virtual IPower , virtual IStage {}; struct IAsyncControl_IPower_IUtility : virtual IAsyncControl , virtual IPower , virtual IUtility {}; struct IAsyncControl_IStage_IUtility : virtual IAsyncControl , virtual IStage , virtual IUtility {}; struct ICommsHandler_IDiagnostics_IDrift : virtual ICommsHandler , virtual IDiagnostics , virtual IDrift {}; struct ICommsHandler_IDiagnostics_IEscape : virtual ICommsHandler , virtual IDiagnostics , virtual IEscape {}; struct ICommsHandler_IDiagnostics_IIllumination : virtual ICommsHandler , virtual IDiagnostics , virtual IIllumination {}; struct ICommsHandler_IDiagnostics_IIris : virtual ICommsHandler , virtual IDiagnostics , virtual IIris {}; struct ICommsHandler_IDiagnostics_IMicroscope : virtual ICommsHandler , virtual IDiagnostics , virtual IMicroscope {}; struct ICommsHandler_IDiagnostics_IObjective : virtual ICommsHandler , virtual IDiagnostics , virtual IObjective {}; struct ICommsHandler_IDiagnostics_IPower : virtual ICommsHandler , virtual IDiagnostics , virtual IPower {}; struct ICommsHandler_IDiagnostics_IStage : virtual ICommsHandler , virtual IDiagnostics , virtual IStage {}; struct ICommsHandler_IDiagnostics_IUtility : virtual ICommsHandler , virtual IDiagnostics , virtual IUtility {}; struct ICommsHandler_IDrift_IEscape : virtual ICommsHandler , virtual IDrift , virtual IEscape {}; struct ICommsHandler_IDrift_IIllumination : virtual ICommsHandler , virtual IDrift , virtual IIllumination {}; struct ICommsHandler_IDrift_IIris : virtual ICommsHandler , virtual IDrift , virtual IIris {}; struct ICommsHandler_IDrift_IMicroscope : virtual ICommsHandler , virtual IDrift , virtual IMicroscope {}; struct ICommsHandler_IDrift_IObjective : virtual ICommsHandler , virtual IDrift , virtual IObjective {}; struct ICommsHandler_IDrift_IPower : virtual ICommsHandler , virtual IDrift , virtual IPower {}; struct ICommsHandler_IDrift_IStage : virtual ICommsHandler , virtual IDrift , virtual IStage {}; struct ICommsHandler_IDrift_IUtility : virtual ICommsHandler , virtual IDrift , virtual IUtility {}; struct ICommsHandler_IEscape_IIllumination : virtual ICommsHandler , virtual IEscape , virtual IIllumination {}; struct ICommsHandler_IEscape_IIris : virtual ICommsHandler , virtual IEscape , virtual IIris {}; struct ICommsHandler_IEscape_IMicroscope : virtual ICommsHandler , virtual IEscape , virtual IMicroscope {}; struct ICommsHandler_IEscape_IObjective : virtual ICommsHandler , virtual IEscape , virtual IObjective {}; struct ICommsHandler_IEscape_IPower : virtual ICommsHandler , virtual IEscape , virtual IPower {}; struct ICommsHandler_IEscape_IStage : virtual ICommsHandler , virtual IEscape , virtual IStage {}; struct ICommsHandler_IEscape_IUtility : virtual ICommsHandler , virtual IEscape , virtual IUtility {}; struct ICommsHandler_IIllumination_IIris : virtual ICommsHandler , virtual IIllumination , virtual IIris {}; struct ICommsHandler_IIllumination_IMicroscope : virtual ICommsHandler , virtual IIllumination , virtual IMicroscope {}; struct ICommsHandler_IIllumination_IObjective : virtual ICommsHandler , virtual IIllumination , virtual IObjective {}; struct ICommsHandler_IIllumination_IPower : virtual ICommsHandler , virtual IIllumination , virtual IPower {}; struct ICommsHandler_IIllumination_IStage : virtual ICommsHandler , virtual IIllumination , virtual IStage {}; struct ICommsHandler_IIllumination_IUtility : virtual ICommsHandler , virtual IIllumination , virtual IUtility {}; struct ICommsHandler_IIris_IMicroscope : virtual ICommsHandler , virtual IIris , virtual IMicroscope {}; struct ICommsHandler_IIris_IObjective : virtual ICommsHandler , virtual IIris , virtual IObjective {}; struct ICommsHandler_IIris_IPower : virtual ICommsHandler , virtual IIris , virtual IPower {}; struct ICommsHandler_IIris_IStage : virtual ICommsHandler , virtual IIris , virtual IStage {}; struct ICommsHandler_IIris_IUtility : virtual ICommsHandler , virtual IIris , virtual IUtility {}; struct ICommsHandler_IMicroscope_IObjective : virtual ICommsHandler , virtual IMicroscope , virtual IObjective {}; struct ICommsHandler_IMicroscope_IPower : virtual ICommsHandler , virtual IMicroscope , virtual IPower {}; struct ICommsHandler_IMicroscope_IStage : virtual ICommsHandler , virtual IMicroscope , virtual IStage {}; struct ICommsHandler_IMicroscope_IUtility : virtual ICommsHandler , virtual IMicroscope , virtual IUtility {}; struct ICommsHandler_IObjective_IPower : virtual ICommsHandler , virtual IObjective , virtual IPower {}; struct ICommsHandler_IObjective_IStage : virtual ICommsHandler , virtual IObjective , virtual IStage {}; struct ICommsHandler_IObjective_IUtility : virtual ICommsHandler , virtual IObjective , virtual IUtility {}; struct ICommsHandler_IPower_IStage : virtual ICommsHandler , virtual IPower , virtual IStage {}; struct ICommsHandler_IPower_IUtility : virtual ICommsHandler , virtual IPower , virtual IUtility {}; struct ICommsHandler_IStage_IUtility : virtual ICommsHandler , virtual IStage , virtual IUtility {}; struct IDiagnostics_IDrift_IEscape : virtual IDiagnostics , virtual IDrift , virtual IEscape {}; struct IDiagnostics_IDrift_IIllumination : virtual IDiagnostics , virtual IDrift , virtual IIllumination {}; struct IDiagnostics_IDrift_IIris : virtual IDiagnostics , virtual IDrift , virtual IIris {}; struct IDiagnostics_IDrift_IMicroscope : virtual IDiagnostics , virtual IDrift , virtual IMicroscope {}; struct IDiagnostics_IDrift_IObjective : virtual IDiagnostics , virtual IDrift , virtual IObjective {}; struct IDiagnostics_IDrift_IPower : virtual IDiagnostics , virtual IDrift , virtual IPower {}; struct IDiagnostics_IDrift_IStage : virtual IDiagnostics , virtual IDrift , virtual IStage {}; struct IDiagnostics_IDrift_IUtility : virtual IDiagnostics , virtual IDrift , virtual IUtility {}; struct IDiagnostics_IEscape_IIllumination : virtual IDiagnostics , virtual IEscape , virtual IIllumination {}; struct IDiagnostics_IEscape_IIris : virtual IDiagnostics , virtual IEscape , virtual IIris {}; struct IDiagnostics_IEscape_IMicroscope : virtual IDiagnostics , virtual IEscape , virtual IMicroscope {}; struct IDiagnostics_IEscape_IObjective : virtual IDiagnostics , virtual IEscape , virtual IObjective {}; struct IDiagnostics_IEscape_IPower : virtual IDiagnostics , virtual IEscape , virtual IPower {}; struct IDiagnostics_IEscape_IStage : virtual IDiagnostics , virtual IEscape , virtual IStage {}; struct IDiagnostics_IEscape_IUtility : virtual IDiagnostics , virtual IEscape , virtual IUtility {}; struct IDiagnostics_IIllumination_IIris : virtual IDiagnostics , virtual IIllumination , virtual IIris {}; struct IDiagnostics_IIllumination_IMicroscope : virtual IDiagnostics , virtual IIllumination , virtual IMicroscope {}; struct IDiagnostics_IIllumination_IObjective : virtual IDiagnostics , virtual IIllumination , virtual IObjective {}; struct IDiagnostics_IIllumination_IPower : virtual IDiagnostics , virtual IIllumination , virtual IPower {}; struct IDiagnostics_IIllumination_IStage : virtual IDiagnostics , virtual IIllumination , virtual IStage {}; struct IDiagnostics_IIllumination_IUtility : virtual IDiagnostics , virtual IIllumination , virtual IUtility {}; struct IDiagnostics_IIris_IMicroscope : virtual IDiagnostics , virtual IIris , virtual IMicroscope {}; struct IDiagnostics_IIris_IObjective : virtual IDiagnostics , virtual IIris , virtual IObjective {}; struct IDiagnostics_IIris_IPower : virtual IDiagnostics , virtual IIris , virtual IPower {}; struct IDiagnostics_IIris_IStage : virtual IDiagnostics , virtual IIris , virtual IStage {}; struct IDiagnostics_IIris_IUtility : virtual IDiagnostics , virtual IIris , virtual IUtility {}; struct IDiagnostics_IMicroscope_IObjective : virtual IDiagnostics , virtual IMicroscope , virtual IObjective {}; struct IDiagnostics_IMicroscope_IPower : virtual IDiagnostics , virtual IMicroscope , virtual IPower {}; struct IDiagnostics_IMicroscope_IStage : virtual IDiagnostics , virtual IMicroscope , virtual IStage {}; struct IDiagnostics_IMicroscope_IUtility : virtual IDiagnostics , virtual IMicroscope , virtual IUtility {}; struct IDiagnostics_IObjective_IPower : virtual IDiagnostics , virtual IObjective , virtual IPower {}; struct IDiagnostics_IObjective_IStage : virtual IDiagnostics , virtual IObjective , virtual IStage {}; struct IDiagnostics_IObjective_IUtility : virtual IDiagnostics , virtual IObjective , virtual IUtility {}; struct IDiagnostics_IPower_IStage : virtual IDiagnostics , virtual IPower , virtual IStage {}; struct IDiagnostics_IPower_IUtility : virtual IDiagnostics , virtual IPower , virtual IUtility {}; struct IDiagnostics_IStage_IUtility : virtual IDiagnostics , virtual IStage , virtual IUtility {}; struct IDrift_IEscape_IIllumination : virtual IDrift , virtual IEscape , virtual IIllumination {}; struct IDrift_IEscape_IIris : virtual IDrift , virtual IEscape , virtual IIris {}; struct IDrift_IEscape_IMicroscope : virtual IDrift , virtual IEscape , virtual IMicroscope {}; struct IDrift_IEscape_IObjective : virtual IDrift , virtual IEscape , virtual IObjective {}; struct IDrift_IEscape_IPower : virtual IDrift , virtual IEscape , virtual IPower {}; struct IDrift_IEscape_IStage : virtual IDrift , virtual IEscape , virtual IStage {}; struct IDrift_IEscape_IUtility : virtual IDrift , virtual IEscape , virtual IUtility {}; struct IDrift_IIllumination_IIris : virtual IDrift , virtual IIllumination , virtual IIris {}; struct IDrift_IIllumination_IMicroscope : virtual IDrift , virtual IIllumination , virtual IMicroscope {}; struct IDrift_IIllumination_IObjective : virtual IDrift , virtual IIllumination , virtual IObjective {}; struct IDrift_IIllumination_IPower : virtual IDrift , virtual IIllumination , virtual IPower {}; struct IDrift_IIllumination_IStage : virtual IDrift , virtual IIllumination , virtual IStage {}; struct IDrift_IIllumination_IUtility : virtual IDrift , virtual IIllumination , virtual IUtility {}; struct IDrift_IIris_IMicroscope : virtual IDrift , virtual IIris , virtual IMicroscope {}; struct IDrift_IIris_IObjective : virtual IDrift , virtual IIris , virtual IObjective {}; struct IDrift_IIris_IPower : virtual IDrift , virtual IIris , virtual IPower {}; struct IDrift_IIris_IStage : virtual IDrift , virtual IIris , virtual IStage {}; struct IDrift_IIris_IUtility : virtual IDrift , virtual IIris , virtual IUtility {}; struct IDrift_IMicroscope_IObjective : virtual IDrift , virtual IMicroscope , virtual IObjective {}; struct IDrift_IMicroscope_IPower : virtual IDrift , virtual IMicroscope , virtual IPower {}; struct IDrift_IMicroscope_IStage : virtual IDrift , virtual IMicroscope , virtual IStage {}; struct IDrift_IMicroscope_IUtility : virtual IDrift , virtual IMicroscope , virtual IUtility {}; struct IDrift_IObjective_IPower : virtual IDrift , virtual IObjective , virtual IPower {}; struct IDrift_IObjective_IStage : virtual IDrift , virtual IObjective , virtual IStage {}; struct IDrift_IObjective_IUtility : virtual IDrift , virtual IObjective , virtual IUtility {}; struct IDrift_IPower_IStage : virtual IDrift , virtual IPower , virtual IStage {}; struct IDrift_IPower_IUtility : virtual IDrift , virtual IPower , virtual IUtility {}; struct IDrift_IStage_IUtility : virtual IDrift , virtual IStage , virtual IUtility {}; struct IEscape_IIllumination_IIris : virtual IEscape , virtual IIllumination , virtual IIris {}; struct IEscape_IIllumination_IMicroscope : virtual IEscape , virtual IIllumination , virtual IMicroscope {}; struct IEscape_IIllumination_IObjective : virtual IEscape , virtual IIllumination , virtual IObjective {}; struct IEscape_IIllumination_IPower : virtual IEscape , virtual IIllumination , virtual IPower {}; struct IEscape_IIllumination_IStage : virtual IEscape , virtual IIllumination , virtual IStage {}; struct IEscape_IIllumination_IUtility : virtual IEscape , virtual IIllumination , virtual IUtility {}; struct IEscape_IIris_IMicroscope : virtual IEscape , virtual IIris , virtual IMicroscope {}; struct IEscape_IIris_IObjective : virtual IEscape , virtual IIris , virtual IObjective {}; struct IEscape_IIris_IPower : virtual IEscape , virtual IIris , virtual IPower {}; struct IEscape_IIris_IStage : virtual IEscape , virtual IIris , virtual IStage {}; struct IEscape_IIris_IUtility : virtual IEscape , virtual IIris , virtual IUtility {}; struct IEscape_IMicroscope_IObjective : virtual IEscape , virtual IMicroscope , virtual IObjective {}; struct IEscape_IMicroscope_IPower : virtual IEscape , virtual IMicroscope , virtual IPower {}; struct IEscape_IMicroscope_IStage : virtual IEscape , virtual IMicroscope , virtual IStage {}; struct IEscape_IMicroscope_IUtility : virtual IEscape , virtual IMicroscope , virtual IUtility {}; struct IEscape_IObjective_IPower : virtual IEscape , virtual IObjective , virtual IPower {}; struct IEscape_IObjective_IStage : virtual IEscape , virtual IObjective , virtual IStage {}; struct IEscape_IObjective_IUtility : virtual IEscape , virtual IObjective , virtual IUtility {}; struct IEscape_IPower_IStage : virtual IEscape , virtual IPower , virtual IStage {}; struct IEscape_IPower_IUtility : virtual IEscape , virtual IPower , virtual IUtility {}; struct IEscape_IStage_IUtility : virtual IEscape , virtual IStage , virtual IUtility {}; struct IIllumination_IIris_IMicroscope : virtual IIllumination , virtual IIris , virtual IMicroscope {}; struct IIllumination_IIris_IObjective : virtual IIllumination , virtual IIris , virtual IObjective {}; struct IIllumination_IIris_IPower : virtual IIllumination , virtual IIris , virtual IPower {}; struct IIllumination_IIris_IStage : virtual IIllumination , virtual IIris , virtual IStage {}; struct IIllumination_IIris_IUtility : virtual IIllumination , virtual IIris , virtual IUtility {}; struct IIllumination_IMicroscope_IObjective : virtual IIllumination , virtual IMicroscope , virtual IObjective {}; struct IIllumination_IMicroscope_IPower : virtual IIllumination , virtual IMicroscope , virtual IPower {}; struct IIllumination_IMicroscope_IStage : virtual IIllumination , virtual IMicroscope , virtual IStage {}; struct IIllumination_IMicroscope_IUtility : virtual IIllumination , virtual IMicroscope , virtual IUtility {}; struct IIllumination_IObjective_IPower : virtual IIllumination , virtual IObjective , virtual IPower {}; struct IIllumination_IObjective_IStage : virtual IIllumination , virtual IObjective , virtual IStage {}; struct IIllumination_IObjective_IUtility : virtual IIllumination , virtual IObjective , virtual IUtility {}; struct IIllumination_IPower_IStage : virtual IIllumination , virtual IPower , virtual IStage {}; struct IIllumination_IPower_IUtility : virtual IIllumination , virtual IPower , virtual IUtility {}; struct IIllumination_IStage_IUtility : virtual IIllumination , virtual IStage , virtual IUtility {}; struct IIris_IMicroscope_IObjective : virtual IIris , virtual IMicroscope , virtual IObjective {}; struct IIris_IMicroscope_IPower : virtual IIris , virtual IMicroscope , virtual IPower {}; struct IIris_IMicroscope_IStage : virtual IIris , virtual IMicroscope , virtual IStage {}; struct IIris_IMicroscope_IUtility : virtual IIris , virtual IMicroscope , virtual IUtility {}; struct IIris_IObjective_IPower : virtual IIris , virtual IObjective , virtual IPower {}; struct IIris_IObjective_IStage : virtual IIris , virtual IObjective , virtual IStage {}; struct IIris_IObjective_IUtility : virtual IIris , virtual IObjective , virtual IUtility {}; struct IIris_IPower_IStage : virtual IIris , virtual IPower , virtual IStage {}; struct IIris_IPower_IUtility : virtual IIris , virtual IPower , virtual IUtility {}; struct IIris_IStage_IUtility : virtual IIris , virtual IStage , virtual IUtility {}; struct IMicroscope_IObjective_IPower : virtual IMicroscope , virtual IObjective , virtual IPower {}; struct IMicroscope_IObjective_IStage : virtual IMicroscope , virtual IObjective , virtual IStage {}; struct IMicroscope_IObjective_IUtility : virtual IMicroscope , virtual IObjective , virtual IUtility {}; struct IMicroscope_IPower_IStage : virtual IMicroscope , virtual IPower , virtual IStage {}; struct IMicroscope_IPower_IUtility : virtual IMicroscope , virtual IPower , virtual IUtility {}; struct IMicroscope_IStage_IUtility : virtual IMicroscope , virtual IStage , virtual IUtility {}; struct IObjective_IPower_IStage : virtual IObjective , virtual IPower , virtual IStage {}; struct IObjective_IPower_IUtility : virtual IObjective , virtual IPower , virtual IUtility {}; struct IObjective_IStage_IUtility : virtual IObjective , virtual IStage , virtual IUtility {}; struct IPower_IStage_IUtility : virtual IPower , virtual IStage , virtual IUtility {};
As you can see, this is a poor solution.
3. Alternatives
3.1. Solution 1 - Constrained Template
Write the function as a template constrained on the required conversions:
template < class T > requires std :: derived_from < T , Editor > && std :: derived_from < T , Processor > void EditAndProcess ( T * const p ) { p -> Edit (); p -> Process (); }
GodBolt for above snippet: https://godbolt.org/z/98YczchPq
3.2. Solution 2 - Multiple Parameters
Pass more than one parameter (i.e. a pointer to each interface), all of which are obtained from the same object, for example:
void EditAndProcess ( Editor * const pE , Processor * const pP ) { pE -> Edit (); pP -> Process (); } int main ( void ) { Emulator e ; EditAndProcess ( & e , & e ); Simulator s ; EditAndProcess ( & s , & s ); }
GodBolt for above snippet: https://godbolt.org/z/ParqrEhbs
3.3. Solution 3 - dynamic_cast
Write a function that takes a pointer to just one of the interfaces, and then use to obtain pointers to the other interfaces:
void EditAndProcess ( Editor * const pE ) { pE -> Edit (); dynamic_cast < Processor *> ( pE ) -> Process (); }
GodBolt for above snippet: https://godbolt.org/z/YdzhGhxzK
3.4. Solution 4 - Adapter
Start off by defining an empty class that derives from both interfaces, and write a function that acts on this new empty class:
struct EditorCumProcessor : Editor , Processor {}; void EditAndProcess ( EditorCumProcessor && ed ) { ed . Edit (); ed . Process (); }
and then provide an adapter class, and use it as so:
template < class T > requires std :: derived_from < T , Editor > && std :: derived_from < T , Processor > class EditorCumProcessorAdapter : public EditorCumProcessor { T * p ; public : explicit EditorCumProcessorAdapter ( T * const arg ) : p ( arg ) {} void Edit ( void ) override { p -> Edit (); } void Process ( void ) override { p -> Process (); } }; int main ( void ) { Emulator e ; EditAndProcess ( EditorCumProcessorAdapter ( & e ) ); }
GodBolt for above snippet: https://godbolt.org/z/a11dzfh9s
3.5. Solution 5 - Improvise the ABI
With in-depth knowledge of the compiler’s ABI, it is possible to get behaviour similar to by composing a specialised v-table at runtime, as follows:
struct Editor { virtual void Edit ( void ) = 0 ; }; struct Processor { virtual void Process ( void ) = 0 ; }; struct Emulator : Editor , Processor { void Edit ( void ) override {} void Process ( void ) override {} }; struct Simulator : Editor , Processor { void Edit ( void ) override {} void Process ( void ) override {} }; struct EditorCumProcessor : virtual Editor , virtual Processor {}; void EditAndProcess ( EditorCumProcessor * const p ) { p -> Edit (); p -> Process (); } template < typename Combined , typename ... Ts > class MakeBeliever final { std :: intptr_t vtable [ 3u + sizeof ...( Ts ) ]{}; // all zero std :: intptr_t const * object = nullptr ; public : template < class T > constexpr MakeBeliever ( T && arg ) noexcept { using std :: intptr_t ; intptr_t * const vptr = vtable + sizeof vtable / sizeof * vtable ; int i = 0 ; auto const mylambda = [ this , vptr , & arg , & i ] < typename B > ( void ) -> void { // In the vtable, set the virtual base's offset intptr_t & offset = vptr [ - 4 - i ++ ]; auto * const pb = std :: addressof ( static_cast < B &> ( arg ) ); offset = ( char * ) pb - ( char * ) & this -> object ; }; ( mylambda . template operator () < Ts > (), ... ); // Set the vptr inside the make-believe object object = vptr ; } constexpr operator Combined * ( void ) noexcept { return static_cast < Combined *> ( static_cast < void *> ( & object ) ); } }; int main ( void ) { Emulator e ; EditAndProcess ( MakeBeliever < EditorCumProcessor , Editor , Processor > ( e ) ); Simulator s ; EditAndProcess ( MakeBeliever < EditorCumProcessor , Editor , Processor > ( s ) ); }
GodBolt for above snippet: https://godbolt.org/z/axsGo1cxY
3.6. Summary of Alternatives
Here are some of the drawbacks to the above five solutions:
-
Templates can balloon code-size (including duplication of internal static variables)
-
A template function prevents having a container of function pointers, e.g.:
std :: vector < bool ( * )( chimeric_ptr < wxControl , wxTextEntry > ) > delegates ; -
Passing the same object as multiple arguments adds complication to the call site
-
will fail at runtime rather than at compile time, any may be too slow in performance-critical code -- furthermore some dynamic casts are ill-formed because of private or ambiguous basesdynamic_cast -
The definition of an adapter class must be edited whenever the interfaces are edited (e.g. methods must be renamed, or new methods added)
-
The ABI improvisation must be re-written for every compiler and may require disabling of optimisations
This goal of this paper is to provide a fast (i.e. minimal CPU cycles) new feature that is very easily manageable, in that changes to client code are seldom required when changes are made to interfaces in 3rd party header files.
4. Interface Resolution
is a template class which you provide with a list of interfaces as follows:
std :: chimeric_ptr < Base1 , Base2 > p ; // starts off as nullptr
The constructor accepts a pointer to an object of type , for example:
Emulator e ; std :: chimeric_ptr < Editor , Processor > p = & e ;
There are 3 ways in which an interface can be gotten from .
4.1. Method 1 - static_upcast
A static upcast conversion is possible when the interface is an unambiguous public base class of (or when the interface is the same type as ).
template < class ToRef , class From > requires is_lvalue_reference_v < ToRef > && ( ! is_reference_v < From > ) && derived_from < From , remove_reference_t < ToRef > > && ( ! is_const_v < From > || is_const_v < remove_reference_t < ToRef > > ) && ( ! is_volatile_v < From > || is_volatile_v < remove_reference_t < ToRef > > ) [[ nodiscard ]] constexpr ToRef static_upcast ( From & arg ) noexcept { return static_cast < ToRef > ( arg ); }
GodBolt for above snippet: https://godbolt.org/z/xKdvPYE7j
4.2. Method 2 - user conversion
This conversion is possible where the class provides :
T var ; var . operator Interface & ();
GodBolt for above snippet: https://godbolt.org/z/hW6616bEW
Note that the above GodBolt is more complicated because it also allows for every combination of and as follows:
operator Interface & (); operator Interface & () const ; operator Interface & () volatile ; operator Interface & () const volatile ; operator Interface const & (); operator Interface const & () const ; operator Interface const & () volatile ; operator Interface const & () const volatile ; and so on . . .
4.3. Method 3 - dynamic_cast
dynamic_cast < Interface *> ( pointer_to_T_object );
GodBolt for above snippet: https://godbolt.org/z/GesMjMbnE
By default, will not perform a dynamic cast -- you must specify the option in order to enable it. A dynamic cast will be possible at runtime if the type is polymorphic and part of an object that inherits from .
5. Member Resolution
Consider the following code snippet:
class Base1 { private : int Func ( double ) { return 0 ; } }; class Base2 { public : float Func ( char ) { return 0.2f ; } }; class Derived : public Base1 , public Base2 {}; int main ( void ) { Derived d ; d . Func ( 'a' ); // ERROR: Ambiguous }
GodBolt for above snippet: https://godbolt.org/z/G6c4KExTY
The above snippet fails to compile because of the ambiguity -- even though is private in .
By default when resolving members, searches for the member in the list of from left to right, and the first match wins.
5.1. Method 1 - chimeric
The default mode for member resolution is Chimeric Mode, where the first class in the Interfaces list with a matching member wins. The following code compiles and runs without error:
#include <memory>// chimeric_ptr class Base1 { private : int Func ( double ) { return 0 ; } }; class Base2 { public : int Func ( void * ) { return 7 ; } }; class Derived : public Base1 , public Base2 {}; int main ( void ) { Derived d ; std :: chimeric_ptr < Base2 , Base1 > p ( & d ); p -> Func ( nullptr ); // But this doesn't work: // p->Func(27.2); }
GodBolt for above snippet: https://godbolt.org/z/x1hn7afGs
Note in the above code the order of the base classes in . If we re-write this as , it will fail to compile because is private, as in the following GodBolt: https://godbolt.org/z/aPKcrx5n6
5.2. Method 2 - forbid ambiguity
If you want to force a compiler error when member resolution is ambiguous, use the option as follows:
std :: chimeric_ptr < std :: forbid_ambiguity , Base1 , Base2 >
For example:
#include <memory>// chimeric_ptr struct Base1 { int Func ( double ) { return 0 ; } }; struct Base2 { int Func ( void * ) { return 7 ; } }; class Derived : public Base1 , public Base2 {}; int main ( void ) { Derived d ; std :: chimeric_ptr < std :: forbid_ambiguity , Base1 , Base2 > p ( & d ); p -> Func ( 56.8 ); // ERROR: 'Func' is ambiguous }
GodBolt for above snippet: https://godbolt.org/z/v6rvofveo
The above snippet fails to compile because is found in more than one interface.
6. Options
6.1. std :: allow_dynamic
bool MyFunction ( std :: chimeric_ptr < std :: allow_dynamic , Base1 , Base2 > const p ) { return p -> Twist () && p -> Stretch (); }
GodBolt for above snippet: https://godbolt.org/z/h5vq57s15
The option, , allows the use of at runtime to try obtain an interface from . Note that the constructor of will throw if a dynamic cast fails.
You cannot combine this option with the option .
6.2. std :: bases_only
bool MyFunction ( std :: chimeric_ptr < std :: bases_only , Base1 , Base2 > const p ) { return p -> Twist () && p -> Stretch (); }
GodBolt for above snippet: https://godbolt.org/z/x88vW1a84
The above GodBolt fails to compile because the option prevents the resolution of the interface .
The option, , ensures at compile-time that all interfaces will be obtained by using .
You cannot combine this option with the option .
6.3. std :: forbid_ambiguity
bool MyFunction ( std :: chimeric_ptr < std :: forbid_ambiguity , Base1 , Base2 > const p ) { return p -> Twist () && p -> Stretch (); }
GodBolt for above snippet: https://godbolt.org/z/cfnMeaKv9
The option, , affects member resolution at compile-time, and ensures at compile-time that a resolved member is only found in one interface.
You cannot combine this option with the option .
7. Implementation
7.1. Compiler Support
Compiler support is required to implement , as shown in the following patch to the GNU g++ compiler which intercepts applied to a chimeric pointer:
https://github.com/healytpk/gcc-thomas-healy/commit/tag_chimeric_ptr
7.2. Class Definition
Although compiler support is required to compensate for the lack of , a possible implementation of the template class is as follows:
#include <cstddef>// nullptr_t, size_t #include <concepts>// derived_from #include <tuple>// get, tuple, tuple_element, tuple_size #include <type_traits>#include <utility>// declval, index_sequence, make_index_sequence, to_underlying namespace std { namespace chimeric_detail { template < class To , class From > requires is_lvalue_reference_v < To > && ( ! is_reference_v < From > ) && derived_from < From , remove_reference_t < To > > && ( ! is_const_v < From > || is_const_v < remove_reference_t < To > > ) && ( ! is_volatile_v < From > || is_volatile_v < remove_reference_t < To > > ) [[ nodiscard ]] constexpr To static_upcast ( From & arg ) noexcept { return static_cast < To > ( arg ); } template < class ToRef , class From > consteval bool user_conversion_detail_noexcept_v ( void ) { typedef remove_cvref_t < ToRef > To ; typedef remove_reference_t < ToRef > ToCV ; if constexpr ( requires ( From & f ) { f . operator ToCV & (); }) // Prefer exact match first (matches C++ "best viable" more closely) return noexcept ( declval < From &> (). operator ToCV & ()); else if constexpr ( requires ( From & f ) { f . operator To & (); }) return noexcept ( declval < From &> (). operator To & ()); else if constexpr ( requires ( From & f ) { f . operator To const & (); } && is_convertible_v < To const * , ToCV *> ) return noexcept ( declval < From &> (). operator To const & ()); else if constexpr ( requires ( From & f ) { f . operator To volatile & (); } && is_convertible_v < To volatile * , ToCV *> ) return noexcept ( declval < From &> (). operator To volatile & ()); else if constexpr ( requires ( From & f ) { f . operator To const volatile & (); } && is_convertible_v < To const volatile * , ToCV *> ) return noexcept ( declval < From &> (). operator To const volatile & ()); else return false; // control should never reach here } template < class ToRef , class From > requires is_lvalue_reference_v < ToRef > && ( ! is_reference_v < From > ) && ( ! is_base_of_v < remove_cvref_t < ToRef > , remove_cv_t < From > > ) && ( requires { declval < From &> (). operator remove_cvref_t < ToRef > & (); } || ( requires { declval < From &> (). operator remove_cvref_t < ToRef > const & (); } && is_convertible_v < remove_cvref_t < ToRef > const * , remove_reference_t < ToRef >* > ) || ( requires { declval < From &> (). operator remove_cvref_t < ToRef > volatile & (); } && is_convertible_v < remove_cvref_t < ToRef > volatile * , remove_reference_t < ToRef >* > ) || ( requires { declval < From &> (). operator remove_cvref_t < ToRef > const volatile & (); } && is_convertible_v < remove_cvref_t < ToRef > const volatile * , remove_reference_t < ToRef >* > ) ) constexpr ToRef user_conversion ( From & arg ) noexcept ( user_conversion_detail_noexcept_v < ToRef , From > () ) { typedef remove_cvref_t < ToRef > To ; typedef remove_reference_t < ToRef > ToCV ; if constexpr ( requires { arg . operator ToCV & (); }) // Prefer exact match first (matches C++ "best viable" more closely) return arg . operator ToCV & (); else if constexpr ( requires { arg . operator To & (); }) return arg . operator To & (); else if constexpr ( requires { arg . operator To const & (); } && is_convertible_v < To const * , ToCV *> ) return arg . operator To const & (); else if constexpr ( requires { arg . operator To volatile & (); } && is_convertible_v < To volatile * , ToCV *> ) return arg . operator To volatile & (); else if constexpr ( requires { arg . operator To const volatile & (); } && is_convertible_v < To const volatile * , ToCV *> ) return arg . operator To const volatile & (); else static_assert ( false, "control should never reach here -- prevented by constraints" ); } template < typename T > concept option = requires { typename T :: __tag_chimera_option ; }; // Canonicalise for duplicate checks but ignore const/volatile template < class T , class ... Us > struct none_same_canon : bool_constant < ( ! is_same_v < remove_cv_t < T > , remove_cv_t < Us > > && ...) > {}; template < class ... Ts > struct unique_canon : true_type {}; template < class T0 , class ... TRest > struct unique_canon < T0 , TRest ... > : bool_constant < none_same_canon < T0 , TRest ... >:: value && unique_canon < TRest ... >:: value > {}; template < class ... Ts > struct has_no_options : bool_constant < ( ! option < Ts > && ...) > {}; // Enforce: All option types must precede all interface types template < class ... Ts > struct options_then_interfaces : true_type {}; template < class T0 , class ... TRest > struct options_then_interfaces < T0 , TRest ... > : bool_constant < option < T0 > ? options_then_interfaces < TRest ... >:: value : has_no_options < TRest ... >:: value > {}; // Convert parameter pack to tuple template < class T , class Tuple > struct tuple_prepend ; template < class T , class ... Ts > struct tuple_prepend < T , tuple < Ts ... > > { using type = tuple < T , Ts ... > ; }; // Filter interfaces out of parameter pack template < class ... InterfacesAndOptions > struct filter_interfaces ; template <> struct filter_interfaces <> { using type = tuple <> ; }; template < class I0 , class ... Rest > struct filter_interfaces < I0 , Rest ... > { using tail = typename filter_interfaces < Rest ... >:: type ; using type = conditional_t < option < I0 > , tail , typename tuple_prepend < I0 , tail >:: type > ; }; // Filter options out of parameter pack template < class ... InterfacesAndOptions > struct filter_options ; template <> struct filter_options <> { using type = tuple <> ; }; template < class I0 , class ... Rest > struct filter_options < I0 , Rest ... > { using tail = typename filter_options < Rest ... >:: type ; using type = conditional_t < option < I0 > , typename tuple_prepend < I0 , tail >:: type , tail > ; }; // Convert tuple into tuple of pointers template < class Tuple > struct tuple_pointerize ; template < class ... Ts > struct tuple_pointerize < tuple < Ts ... > > { using type = tuple < Ts * ... > ; }; template < class B , class Tuple > struct tuple_contains_type : false_type {}; template < class B , class ... Ts > struct tuple_contains_type < B , tuple < Ts ... > > : bool_constant < ( is_same_v < B , Ts > || ...) > {}; // Option tuple must have no const/volatile template < class Tuple > struct tuple_all_uncv : false_type {}; template < class ... Ts > struct tuple_all_uncv < tuple < Ts ... > > : bool_constant < ( is_same_v < Ts , remove_cv_t < Ts > > && ...) > {}; // For alphabetical order: option types define // typedef integral_constant<unsigned, N> __tag_chimera_option; template < class T , class = void > struct option_key {}; template < class T > struct option_key < T , void_t < decltype ( T :: __tag_chimera_option :: value ) > > : integral_constant < unsigned , T :: __tag_chimera_option :: value > {}; template < class ... Ts > struct is_option_pack_alphabetical : true_type {}; template < class T0 , class T1 , class ... Ts > struct is_option_pack_alphabetical < T0 , T1 , Ts ... > : bool_constant < ( option_key < T0 >:: value < option_key < T1 >:: value ) && is_option_pack_alphabetical < T1 , Ts ... >:: value > {}; template < class Tuple > struct is_option_tuple_alphabetical : false_type {}; template < class ... Ts > struct is_option_tuple_alphabetical < tuple < Ts ... > > : is_option_pack_alphabetical < Ts ... > {}; template < class ... Ts > struct is_chimeric_ptr : false_type {}; template < class ... Ts > struct any_is_chimeric_ptr : bool_constant < ( is_chimeric_ptr < remove_cvref_t < Ts > >:: value || ...) > {}; template < class Want , class Tuple > struct tuple_find_uncv ; template < class Want > struct tuple_find_uncv < Want , tuple <>> { using type = void ; }; template < class Want , class T0 , class ... Ts > struct tuple_find_uncv < Want , tuple < T0 , Ts ... >> { using type = conditional_t < is_same_v < remove_cv_t < T0 > , remove_cv_t < Want >> , T0 , typename tuple_find_uncv < Want , tuple < Ts ... >>:: type > ; }; template < class Want , class Tuple > using tuple_find_uncv_t = typename tuple_find_uncv < Want , Tuple >:: type ; template < class I , class RhsTuple > concept rhs_supplies_interface = ( ! is_void_v < tuple_find_uncv_t < I , RhsTuple >> ) && is_convertible_v < tuple_find_uncv_t < I , RhsTuple >* , I *> ; template < class LhsTuple , class RhsTuple > concept rhs_supplies_all = [] < class ... Is > ( type_identity < tuple < Is ... >> ) consteval { return ( rhs_supplies_interface < Is , RhsTuple > && ...); }( type_identity < LhsTuple > {}); } // namespace chimeric_detail struct allow_dynamic { using __tag_chimera_option = integral_constant < unsigned , 10u > ; }; struct bases_only { using __tag_chimera_option = integral_constant < unsigned , 20u > ; }; struct forbid_ambiguity { using __tag_chimera_option = integral_constant < unsigned , 30u > ; }; template < class ... InterfacesAndOptions > class chimeric_ptr final { typedef int __tag_chimera ; template < class ... > friend class chimeric_ptr ; static_assert ( sizeof ...( InterfacesAndOptions ) > 0u , "std::chimeric_ptr must have at least one template parameter type" ); static_assert (( is_class_v < InterfacesAndOptions > && ...), "std::chimeric_ptr: all template parameters must be class types (no pointers, no references)" ); static_assert ( chimeric_detail :: unique_canon < InterfacesAndOptions ... >:: value , "std::chimeric_ptr: duplicate template parameters are not permitted after removing const/volatile" ); static_assert ( chimeric_detail :: options_then_interfaces < InterfacesAndOptions ... >:: value , "std::chimeric_ptr: all options must precede all interfaces in template parameter list" ); static_assert ( ! chimeric_detail :: any_is_chimeric_ptr < InterfacesAndOptions ... >:: value , "std::chimeric_ptr cannot be used as a template argument to std::chimeric_ptr" ); typedef typename chimeric_detail :: filter_interfaces < InterfacesAndOptions ... >:: type ChimericInterfacesTuple_t ; typedef typename chimeric_detail :: filter_options < InterfacesAndOptions ... >:: type ChimericOptionsTuple_t ; static_assert ( tuple_size < ChimericInterfacesTuple_t >:: value > 0u , "std::chimeric_ptr must have at least one interface" ); static_assert ( chimeric_detail :: tuple_all_uncv < ChimericOptionsTuple_t >:: value , "std::chimeric_ptr: option types must not be const/volatile" ); static_assert ( chimeric_detail :: is_option_tuple_alphabetical < ChimericOptionsTuple_t >:: value , "std::chimeric_ptr: options must be in alphabetical order" ); inline static constexpr bool opt_allow_dynamic = chimeric_detail :: tuple_contains_type < allow_dynamic , ChimericOptionsTuple_t >:: value , opt_bases_only = chimeric_detail :: tuple_contains_type < bases_only , ChimericOptionsTuple_t >:: value , opt_forbid_ambiguity = chimeric_detail :: tuple_contains_type < forbid_ambiguity , ChimericOptionsTuple_t >:: value ; static_assert ( false== ( opt_allow_dynamic && opt_bases_only ), "std::chimeric_ptr: options 'allow_dynamic' and 'bases_only' cannot be combined" ); typename chimeric_detail :: tuple_pointerize < ChimericInterfacesTuple_t >:: type __chimeric_addresses {}; // start off all nullptr enum class HowInterfaceGotten : int { not_possible = 0 , static_upcast = + 1 , user_conversion_nothrow = + 2 , user_conversion_throw = -2 , dynamic = -3 , }; template < class T , class Interface > static consteval HowInterfaceGotten HowToGet ( void ) noexcept { if constexpr ( requires ( T & t ){ chimeric_detail :: static_upcast < Interface &> ( t ); } ) { return HowInterfaceGotten :: static_upcast ; } else if constexpr ( opt_bases_only ) { // set_from will not use conversion operators when 'bases_only' is set // (and 'allow_dynamic' cannot be combined with 'bases_only') return HowInterfaceGotten :: not_possible ; } else if constexpr ( requires ( T & t ){ chimeric_detail :: user_conversion < Interface &> ( t ); } ) { if constexpr ( noexcept ( chimeric_detail :: user_conversion < Interface &> ( declval < T &> ())) ) return HowInterfaceGotten :: user_conversion_nothrow ; else return HowInterfaceGotten :: user_conversion_throw ; } else if constexpr ( opt_allow_dynamic && is_polymorphic_v < T > && requires ( T & t ){ dynamic_cast < Interface &> ( t ); }) { return HowInterfaceGotten :: dynamic ; } else { return HowInterfaceGotten :: not_possible ; } } template < typename T , size_t ... Is > static consteval bool CanAnyThrow ( index_sequence < Is ... > ) noexcept { constexpr auto mylambda = [] < typename Interface > ( void ) constexpr noexcept -> bool { return to_underlying ( HowToGet < T , Interface > () ) < 0 ; }; return ( mylambda . template operator () < tuple_element_t < Is , ChimericInterfacesTuple_t > > () || ... ); } template < typename T > static consteval bool CanAnyThrow ( void ) noexcept { return CanAnyThrow < T > ( make_index_sequence < tuple_size_v < ChimericInterfacesTuple_t > > {}); } template < class T , size_t ... Is > constexpr void set_from ( T * p , index_sequence < Is ... > ) noexcept ( ! CanAnyThrow < T > () ) { constexpr auto mylambda = [] < typename Interface > ( Interface *& pI , T * const p2 ) constexpr { if constexpr ( requires { pI = __builtin_addressof ( chimeric_detail :: static_upcast < Interface &> ( * p2 ) ); } ) { pI = __builtin_addressof ( chimeric_detail :: static_upcast < Interface &> ( * p2 ) ); } else if constexpr ( opt_bases_only ) { static_assert ( false, "std::chimeric_ptr : cannot get interface because of option 'bases_only'" ); } else if constexpr ( requires { pI = __builtin_addressof ( chimeric_detail :: user_conversion < Interface &> ( * p2 ) ); } ) { pI = __builtin_addressof ( chimeric_detail :: user_conversion < Interface &> ( * p2 ) ); } else if constexpr ( opt_allow_dynamic ) { if constexpr ( ! is_polymorphic_v < T > ) { static_assert ( false, "std::chimeric_ptr : cannot get interface because T is not polymorphic (dynamic allowed)" ); } else if constexpr ( false== requires { pI = __builtin_addressof ( dynamic_cast < Interface &> ( * p2 ) ); } ) { static_assert ( false, "std::chimeric_ptr : cannot get interface because dynamic_cast is ill-formed" ); } else { pI = __builtin_addressof ( dynamic_cast < Interface &> ( * p2 ) ); // might throw 'std::bad_cast' } } else { static_assert ( false, "std::chimeric_ptr : cannot get interface from supplied object (dynamic disallowed)" ); } }; ( mylambda . template operator () < tuple_element_t < Is , ChimericInterfacesTuple_t > > ( get < Is > ( __chimeric_addresses ), p ), ...); } template < class ... Ts > inline static constexpr bool __compatible_rhs = ( ! std :: same_as < chimeric_ptr , chimeric_ptr < Ts ... > > ) && chimeric_detail :: rhs_supplies_all < ChimericInterfacesTuple_t , typename chimeric_ptr < Ts ... >:: ChimericInterfacesTuple_t > ; template < class I , class RhsInterfacesTuple , class ... Ts > static constexpr I * __get_from_rhs ( chimeric_ptr < Ts ... > const & rhs ) noexcept { using J = chimeric_detail :: tuple_find_uncv_t < I , RhsInterfacesTuple > ; // First get J* from rhs via its public conversion operator, then cv-safe convert to I*. return static_cast < I *> ( static_cast < J *> ( rhs ) ); } template < class ... Ts > constexpr void __copy_ptrs_from ( chimeric_ptr < Ts ... > const & rhs ) noexcept { typedef typename chimeric_ptr < Ts ... >:: ChimericInterfacesTuple_t RhsInterfacesTuple ; if ( ! rhs ) { __chimeric_addresses = {}; return ; } auto const mylambda = [ & ] < class ... Is > ( type_identity < tuple < Is ... > > ) constexpr { (( get < Is *> ( __chimeric_addresses ) = __get_from_rhs < Is , RhsInterfacesTuple > ( rhs )), ...); }; mylambda ( type_identity < ChimericInterfacesTuple_t > {} ); } public : constexpr chimeric_ptr ( void ) noexcept = default ; constexpr chimeric_ptr ( nullptr_t ) noexcept {} constexpr chimeric_ptr ( chimeric_ptr const & ) noexcept = default ; constexpr chimeric_ptr ( chimeric_ptr && ) noexcept = default ; constexpr chimeric_ptr & operator = ( chimeric_ptr const & ) noexcept = default ; constexpr chimeric_ptr & operator = ( chimeric_ptr && ) noexcept = default ; template < class T > /* implicit */ constexpr chimeric_ptr ( T * const p ) noexcept ( ! CanAnyThrow < T > () ) { static_assert ( is_class_v < T > , "std::chimeric_ptr : constructor argument must be pointer to class" ); if ( nullptr == p ) return ; set_from ( p , make_index_sequence < tuple_size_v < ChimericInterfacesTuple_t > > {} ); } constexpr chimeric_ptr & operator = ( nullptr_t ) noexcept { __chimeric_addresses = {}; return * this ; } explicit constexpr operator bool ( void ) const noexcept { return nullptr != get < 0u > ( __chimeric_addresses ); } constexpr bool operator ! ( void ) const noexcept { return ! static_cast < bool > ( * this ); } friend constexpr bool operator == ( chimeric_ptr const & p , nullptr_t ) noexcept { return ! p ; } friend constexpr bool operator == ( nullptr_t , chimeric_ptr const & p ) noexcept { return ! p ; } friend constexpr bool operator != ( chimeric_ptr const & p , nullptr_t ) noexcept { return !! p ; } friend constexpr bool operator != ( nullptr_t , chimeric_ptr const & p ) noexcept { return !! p ; } template < class B > requires chimeric_detail :: tuple_contains_type < B , ChimericInterfacesTuple_t >:: value constexpr operator B * ( void ) const noexcept { return get < B *> ( __chimeric_addresses ); } // Converting copy-constructor template < class ... Ts > requires __compatible_rhs < Ts ... > constexpr chimeric_ptr ( chimeric_ptr < Ts ... > const & original ) noexcept { __copy_ptrs_from ( original ); } // Converting move-constructor (raw pointers: same as copy, but we can null out rhs) template < class ... Ts > requires __compatible_rhs < Ts ... > constexpr chimeric_ptr ( chimeric_ptr < Ts ... > && original ) noexcept { __copy_ptrs_from ( original ); original = nullptr ; } // Converting copy-assignment template < class ... Ts > requires __compatible_rhs < Ts ... > constexpr chimeric_ptr & operator = ( chimeric_ptr < Ts ... > const & rhs ) noexcept { __copy_ptrs_from ( rhs ); return * this ; } // Converting move-assignment template < class ... Ts > requires __compatible_rhs < Ts ... > constexpr chimeric_ptr & operator = ( chimeric_ptr < Ts ... > && rhs ) noexcept { __copy_ptrs_from ( rhs ); rhs = nullptr ; return * this ; } }; namespace chimeric_detail { template < class ... Ts > struct is_chimeric_ptr < chimeric_ptr < Ts ... > > : true_type {}; } // namespace chimeric_detail } // namespace std
8. Design Considerations
-
is non-owning and does not manage lifetime.std :: chimeric_ptr < Interfaces ... > -
Construction is permitted from
only ifT * can provide eachT * .Interfaces * ... -
In left to right order, all options must appear before all interfaces without any duplicates. (Two classes are considered the same even if they have different cv-qualifiers).
-
Options must be specified in alphabetical order.
-
performs ordered lookup inp -> name and when found, binds as if byInterfaces ... .static_cast < Interface *> ( p ) -> name -
The selection is by name presence, not by call viability; overload resolution is performed only within the selected interface.
-
Compile-time options can alter the behaviour of the chimeric pointer (e.g. allow
to find an interface).dynamic_cast -
The facility is intended to reduce API ballooning in interface-heavy code without forcing templates, adapters, or ABI improvisation.
9. Impact on the Standard
This proposal introduces a single, header-only library extension to (), and adds no new keywords. No pre-exisiting code is broken by this change.