Audacity 3.2.0
Classes | Public Types | Public Member Functions | Static Public Member Functions | Private Types | Static Private Member Functions | List of all members
AttachedVirtualFunction< Tag, Return, This, Arguments > Class Template Reference

Class template generates single-dispatch, open method registry tables. More...

#include <AttachedVirtualFunction.h>

Inheritance diagram for AttachedVirtualFunction< Tag, Return, This, Arguments >:
[legend]

Classes

struct  Entry
 Member of registry of implementations of the method. More...
 
struct  Override
 For defining overrides of the method. More...
 

Public Types

using Object = This
 This member name is declared in this class template and redeclared in each override. More...
 
using Function = std::function< Return(Object &, Arguments...) >
 This member name is declared in this class template and redeclared in each override. More...
 

Public Member Functions

 AttachedVirtualFunction ()
 At least one static instance must be created; more instances are harmless. More...
 

Static Public Member Functions

static Function Implementation ()
 A function returning a std::function, which you must define so that the program links. More...
 
static Return Call (This &obj, Arguments ...arguments)
 Invoke the method – but only after static initialization time. More...
 

Private Types

using Predicate = std::function< bool(This *) >
 
using Registry = std::vector< Entry >
 

Static Private Member Functions

template<typename Subclass >
static void Register (const Function &function)
 
static RegistryGetRegistry ()
 

Detailed Description

template<typename Tag, typename Return, typename This, typename... Arguments>
class AttachedVirtualFunction< Tag, Return, This, Arguments >

Class template generates single-dispatch, open method registry tables.

Defines a "virtual" function with multiple bodies chosen by type-switch on the runtime class of the first argument, leaving the set of overrides open-ended for extension, but also requiring no modification of the definition of the base class of the type hierarchy that is switched on.

The invocation of the function is not as efficient as for true virtual functions but the advantage of this utility is greater compilation decoupling. Client code can attach its own virtual functions, non-intrusively, to the root of a class hierarchy defined in the core.

There is no collection of overriding function pointers into virtual function tables by the linker, but a registration of overrides into a table at static initialization time. This allows those implementations to be defined wherever is convenient, even in dynamically loaded libraries.

Beware that invocation of the function should not be done during initialization of file scope static objects. Dispatch might not go to the correct subclass case if initializations are not yet complete.

Example usage:

A core class:

// file Host.h
class AbstractHost
{
// ...
};

Declare the attached function (in a header that Host.cpp need not include at all) as a specialization of the template:

// file Client.h
enum class ErrorCode { Ok, Bad, // ...
}; // a return type for our function
// First this empty structure serving just to distinguish among instantiations
// of AttachedVirtualFunction with otherwise identical parameters
// An incomplete type is enough
struct DoSomethingTag;
// Now declare the "virtual function"
using DoSomething =
DoSomethingTag,
ErrorCode,
AbstractHost, // class to be switched on by runtime type
int, double // other arguments
>;
// Allow correct linkage for overrides defined in dynamically loaded modules:
DECLARE_EXPORTED_ATTACHED_VIRTUAL(AUDACITY_DLL_API, DoSomething);
#define DECLARE_EXPORTED_ATTACHED_VIRTUAL(DECLSPEC, Name)
Typically follow the using declaration of a new AttachedVirtualFunction with this macro.
Class template generates single-dispatch, open method registry tables.
@ Ok
One button.

Definitions needed:

//file Client.cpp
// Define the default function body here
return [](AbstractHost &host, int arg1, double arg2) {
return ErrorCode::Ok;
};
// or you could return nullptr instead of a lambda to force InconsistencyException
// at runtime if the virtual function is invoked for a host subclass for which no override
// was defined.
}
#define DEFINE_ATTACHED_VIRTUAL(Name)
Used in the companion .cpp file to the .h using the above macro; followed by a function body.

Usage of the method somewhere else:

#include "Client.h"
void UseDoSomething( AbstractHost &host )
{
// ...
auto error = DoSomething::Call( host, 0, 1.0 );
// ...
}

Derived classes from AbstractHost, not needing Client.h:

// file SpecialHost.h
#include "Host.h"
class SpecialHost : public AbstractHost
{
// ...
};
class ExtraSpecialHost : public SpecialHost
{
// ...
};

Overrides of the method, defined in any other .cpp file:

#include "SpecialHost.h"
#include "Client.h"
// An override of the function, building up a hierarchy of function bodies parallel
// to the host class hierarchy
using DoSomethingSpecial = DoSomething::Override< SpecialHost >;
DEFINE_ATTACHED_VIRTUAL_OVERRIDE(DoSomethingSpecial) {
// The function can be defined without casting the first argument
return [](SpecialHost &host, int arg1, double arg2) {
return arg1 == 0 ? ErrorCode::Ok : ErrorCode::Bad;
};
}
static DoSomethingSpecial registerMe;
// A further override, demonstrating call-through too
using DoSomethingExtraSpecial =
DoSomething::Override< ExtraSpecialHost, DoSomethingSpecial >;
DEFINE_ATTACHED_VIRTUAL_OVERRIDE(DoSomethingExtraSpecial) {
return [](ExtraSpecialHost &host, int arg1, double arg2){
// Call the immediately overridden version of the function
auto result = Callthrough( host, arg1, arg2 );
if ( result == ErrorCode::OK ) {
if ( arg2 != 1066.0 )
result = ErrorCode::Bad;
}
return result;
};
}
static DoSomethingExtraSpecial registerMe;
#define DEFINE_ATTACHED_VIRTUAL_OVERRIDE(Name)
Used to define overriding function; followed by a function body.
Template Parameters
Tagan incomplete type, to distinguish methods with otherwise identical parameters
Returnthe value returned by each override
Thistype of the first argument, a class with at least one true virtual function, the root of the hierarchy for the run-time type-switch
Argumentsany number of types for the second and later arguments

Definition at line 158 of file AttachedVirtualFunction.h.

Member Typedef Documentation

◆ Function

template<typename Tag , typename Return , typename This , typename... Arguments>
using AttachedVirtualFunction< Tag, Return, This, Arguments >::Function = std::function< Return( Object&, Arguments... ) >

This member name is declared in this class template and redeclared in each override.

Definition at line 165 of file AttachedVirtualFunction.h.

◆ Object

template<typename Tag , typename Return , typename This , typename... Arguments>
using AttachedVirtualFunction< Tag, Return, This, Arguments >::Object = This

This member name is declared in this class template and redeclared in each override.

Definition at line 163 of file AttachedVirtualFunction.h.

◆ Predicate

template<typename Tag , typename Return , typename This , typename... Arguments>
using AttachedVirtualFunction< Tag, Return, This, Arguments >::Predicate = std::function< bool( This* ) >
private

Definition at line 267 of file AttachedVirtualFunction.h.

◆ Registry

template<typename Tag , typename Return , typename This , typename... Arguments>
using AttachedVirtualFunction< Tag, Return, This, Arguments >::Registry = std::vector< Entry >
private

Definition at line 276 of file AttachedVirtualFunction.h.

Constructor & Destructor Documentation

◆ AttachedVirtualFunction()

template<typename Tag , typename Return , typename This , typename... Arguments>
AttachedVirtualFunction< Tag, Return, This, Arguments >::AttachedVirtualFunction ( )

At least one static instance must be created; more instances are harmless.

(There will be others if there are any overrides.)

Member Function Documentation

◆ Call()

template<typename Tag , typename Return , typename This , typename... Arguments>
static Return AttachedVirtualFunction< Tag, Return, This, Arguments >::Call ( This &  obj,
Arguments ...  arguments 
)
inlinestatic

Invoke the method – but only after static initialization time.

Parameters
objObject on which to type-switch at run-time
argumentsother arguments

Definition at line 223 of file AttachedVirtualFunction.h.

227 {
228 try {
229 // Note that the constructors of this class and overrides cause
230 // the registry to be topologically sorted, with functions for
231 // less-derived classes earlier in the table; so take the last
232 // one that matches the object. (The object might not be of the exact
233 // class corresponding to any of the overrides, which is why this
234 // solution involves calling the predicates generated in Register,
235 // and wouldn't work just with hashing on std::type_index; but perhaps
236 // such a cache could be memo-ized)
237 auto &registry = GetRegistry();
238 auto iter = registry.rbegin(), end = registry.rend();
239 for ( ; iter != end; ++iter ) {
240 auto &entry = *iter;
241 if ( entry.predicate( &obj ) )
242 // This might throw std::bad_function_call on a null function
243 return entry.function(
244 obj, std::forward< Arguments >( arguments )... );
245 }
246 // If not found, also throw
247 throw std::bad_function_call{};
248 }
249 catch ( const std::bad_function_call& ) {
250 // No matching case found with a non-null function.
251 // Translate the exception
253 }
254 }
static ProjectFileIORegistry::AttributeWriterEntry entry
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
static Registry & GetRegistry()
const char * end(const char *str) noexcept
Definition: StringUtils.h:106

References details::end(), entry, AttachedVirtualFunction< Tag, Return, This, Arguments >::GetRegistry(), and THROW_INCONSISTENCY_EXCEPTION.

Referenced by ChannelView::ChannelView(), TimeShiftHandle::Click(), anonymous_namespace{ClipMenus.cpp}::DoClipMove(), DoProjectTempoChange(), ClipMoveState::Init(), anonymous_namespace{SyncLock.cpp}::IsSeparatorTrack(), and anonymous_namespace{SyncLock.cpp}::IsSyncLockableNonSeparatorTrack().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetRegistry()

template<typename Tag , typename Return , typename This , typename... Arguments>
static Registry & AttachedVirtualFunction< Tag, Return, This, Arguments >::GetRegistry ( )
staticprivate

Referenced by AttachedVirtualFunction< Tag, Return, This, Arguments >::Call(), and AttachedVirtualFunction< Tag, Return, This, Arguments >::Register().

Here is the caller graph for this function:

◆ Implementation()

template<typename Tag , typename Return , typename This , typename... Arguments>
static Function AttachedVirtualFunction< Tag, Return, This, Arguments >::Implementation ( )
static

A function returning a std::function, which you must define so that the program links.

It may return nullptr in case this must act somewhat as a "pure virtual", throwing InconsistencyException if the function is invoked on a subclass for which no override was defined

◆ Register()

template<typename Tag , typename Return , typename This , typename... Arguments>
template<typename Subclass >
static void AttachedVirtualFunction< Tag, Return, This, Arguments >::Register ( const Function function)
inlinestaticprivate

Definition at line 258 of file AttachedVirtualFunction.h.

259 {
260 // Push back a dynamic type test and corresponding function body
261 GetRegistry().push_back({
262 []( This *b ){ return dynamic_cast< Subclass * >( b ) != nullptr; },
263 function
264 });
265 }

References AttachedVirtualFunction< Tag, Return, This, Arguments >::GetRegistry().

Here is the call graph for this function:

The documentation for this class was generated from the following file: