//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _ENGINEOBJECT_H_
#define _ENGINEOBJECT_H_
#ifndef _ENGINETYPES_H_
#include "console/engineTypes.h"
#endif
#ifndef _REFBASE_H_
#include "core/util/refBase.h"
#endif
/// Disable TMM for this file.
#include "platform/tmm_off.h"
/// @file
/// This file contains the framework for defining class types for interfacing
/// through engine API.
class EngineObject;
/// Assign the current class and all its subclasses to the given export scope.
/// May be overridden by subclasses.
///
/// @code
/// class MyClass : public EngineObject
/// {
/// DECLARE_CLASS( MyClass, EngineObject );
/// DECLARE_INSCOPE( MyAPI );
/// };
/// @endcode
#define DECLARE_INSCOPE( name ) \
typedef name __DeclScope;
/// Declare that this class and all its subclasses cannot be instantiated through
/// the API, i.e. objects of these classes can only be created inside the engine.
///
/// @code
/// class MyClass : public EngineObject
/// {
/// DECLARE_CLASS( MyClass, EngineObject );
/// DECLARE_NONINSTANTIABLE;
/// };
/// @endcode
#define DECLARE_NONINSTANTIABLE \
typedef ::FalseType __IsInstantiableType;
/// Declare that this class and all its subclasses can be instantiated through the
/// API using their respective create() functions.
///
/// May be overridden by subclasses.
///
/// @code
/// class MyClass : public EngineObject
/// {
/// DECLARE_CLASS( MyClass, EngineObject );
/// DECLARE_INSTANTIABLE;
/// };
/// @endcode
#define DECLARE_INSTANTIABLE \
typedef ::TrueType __IsInstantiableType;
/// Declare a class for which the object's may be forcefully disposed of by the engine.
/// In essense, this means that such objects can only be weak-referenced from within the
/// control layer as their lifetime is ultimately controlled by the engine. This is
/// used for all resource type objects that are owned by their devices.
///
/// An alternative to disposable types would be to shield the control layer from direct
/// access to these objects and rather expose only intermediary objects. However, at some
/// point this just gets crazy. The control layer wraps around engine objects that wrap
/// around internal objects. The internal object goes away, invalidates the native wrapper
/// which invalidates the managed wrapper.
///
/// By exposing the control layer directly to in-engine resources using disposable objects,
/// intermediary steps and code are made unnecessary while the control layer is still made
/// aware of the fact that ownership of these objects ultimately and firmly belongs with
/// the engine.
///
/// One important guarantee for disposable types is that an object will not go
/// away unless the control layer calls into the engine. This means that these
/// objects will not magically disappear while managed code is running.
///
/// @note This macro automatically redefines destroySelf().
/// @warn Reference-counting is still used for disposable types!! This means that if the
/// reference-count drops to zero, the object will be deleted as any other object.
///
/// @see IMPLEMENT_DISPOSABLE
#define DECLARE_DISPOSABLE \
protected: \
typedef ::TrueType __IsDisposableType; \
DECLARE_CALLBACK( void, onDispose, () ); \
public: \
virtual void destroySelf() \
{ \
onDispose_callback(); \
SuperType::destroySelf(); \
}
/// Matching implement for DECLARE_DISPOSABLE.
///
/// @param type The disposable C++ class type.
#define IMPLEMENT_DISPOSABLE( type ) \
IMPLEMENT_NEW_CALLBACK( type, onDispose, void, (), (), \
"Called before the instance is disposed." );
/// Declare that the current class (and any of its descendents) can only have a single instance.
///
/// @code
/// class MySingletonClass : public EngineObject
/// {
/// DECLARE_CLASS( MySingletonClass, EngineObject );
/// DECLARE_SINGLETON;
/// };
/// @endcode
///
/// @note At the moment, DECLARE_SINGLETON disallows the use of custom create methods
/// on the same class.
#define DECLARE_SINGLETON \
private: \
static ThisType* _smInstance; \
struct _clearInstance; \
friend struct _clearInstance; \
struct _clearInstance { \
~_clearInstance() { _smInstance = NULL; } \
} _clearInstanceInst; \
public: \
static ThisType* createSingleton(); \
static void destroySingleton(); \
protected: \
typedef ::TrueType __IsSingletonType; \
DEFINE_CREATE_METHOD \
{ \
return createSingleton(); \
} \
public: \
static ThisType* instance() \
{ \
return _smInstance; \
}
/// Matching implement for DECLARE_SINGLETON.
/// @param type The C++ class type.
#define IMPLEMENT_SINGLETON( type ) \
type::ThisType* type::_smInstance; \
type::ThisType* type::createSingleton() \
{ \
if( _smInstance != NULL ) \
{ \
Con::errorf( "%s::create - Singleton instance already created", \
TYPE< ThisType >()->getFullyQualifiedExportName().c_str() ); \
return NULL; \
} \
_smInstance = new ThisType(); \
_smInstance->incRefCount(); \
return _smInstance; \
} \
void type::destroySingleton() \
{ \
if( !_smInstance ) \
return; \
if( ::IsTrueType< __IsDisposableType >() ) \
_smInstance->destroySelf(); \
else \
_smInstance->decRefCount(); \
} \
DefineNewEngineStaticMethod( type, getInstance, type*, (),, \
"Get the singleton " #type " instance.\n\n" \
"@return The " #type " singleton." ) \
{ \
return type::instance(); \
}
/// Declare a static class, i.e. a class which only acts as an export scope. Static
/// classes cannot be instantiated, neither through the API nor inside the engine (at
/// least in a form that would be usable within the API).
///
/// @code
/// class MyFunctions
/// {
/// DECLARE_STATIC_CLASS( MyFunctions );
/// };
///
/// IMPLEMENT_STATIC_CLASS( MyFunctions,, "doc" );
///
/// DefineStaticEngineMethod( MyFunctions, doSomething, void, (),, "" )
/// {
/// // ...
/// }
/// @endcode
///
/// @param type The C++ class type.
///
/// @see IMPLEMENT_STATIC_CLASS
#define DECLARE_STATIC_CLASS( type ) \
private: \
static EngineExportScope __engineExportScopeInst; \
static EngineExportScope& __engineExportScope() \
{ return __engineExportScopeInst; } \
public: \
template< typename T > friend struct ::_SCOPE; \
/// Matching implement for DECLARE_STATIC_CLASS.
///
/// @param type The C++ type of the static class. Also used as export name.
/// @param scope Export scope to place the static class in.
/// @param doc Documentation string.
#define IMPLEMENT_STATIC_CLASS( type, scope, doc ) \
EngineExportScope type::__engineExportScopeInst( #type, &_SCOPE< scope >()(), doc );
/// Declare a concrete class @a type derived from the class @a super. Whether the
/// class can be instantiated by the control layer depends on its instantiability.
///
/// @code
/// class MyClass : public EngineObject
/// {
/// DECLARE_CLASS( MyClass, EngineObject );
/// };
/// @endcode
///
/// @param type C++ class type.
/// @param super C++ class type of the superclass. May be void only for EngineObject.
///
/// @see IMPLEMENT_CLASS
/// @see IMPLEMENT_NONINSTANTIABLE_CLASS
#define DECLARE_CLASS( type, super ) \
public: \
typedef type ThisType; \
typedef super SuperType; \
template< typename T > friend struct ::_EngineTypeTraits; \
template< typename T > friend struct ::_SCOPE; \
template< typename T > friend T* _CREATE(); \
template< typename T, typename Base > friend class ::EngineClassTypeInfo; \
private: \
typedef ::_Private::_ConcreteClassBase< ThisType > _ClassBase; \
static EngineClassTypeInfo< ThisType, _ClassBase > _smTypeInfo; \
static EngineExportScope& __engineExportScope(); \
static EnginePropertyTable& _smPropertyTable; \
virtual const EngineTypeInfo* __typeinfo() const; \
public:
/// Declare an abstract class @a type derived from the class @a super.
///
/// @code
/// class MyClass : public EngineObject
/// {
/// DECLARE_ABSTRACT_CLASS( MyClass, EngineObject );
/// };
/// @endcode
///
/// @param type C++ class type.
/// @param super C++ class type of the superclass. May be void only for EngineObject.
///
/// @see IMPLEMENT_NONINSTANTIABLE_CLASS
#define DECLARE_ABSTRACT_CLASS( type, super ) \
public: \
typedef type ThisType; \
typedef super SuperType; \
template< typename T > friend struct ::_EngineTypeTraits; \
template< typename T > friend struct ::_SCOPE; \
template< typename T > friend T* _CREATE(); \
template< typename T, typename Base > friend class ::EngineClassTypeInfo; \
private: \
typedef ::_Private::_AbstractClassBase< ThisType > _ClassBase; \
static EngineClassTypeInfo< ThisType, _ClassBase > _smTypeInfo; \
static EngineExportScope& __engineExportScope(); \
static EnginePropertyTable& _smPropertyTable; \
virtual const EngineTypeInfo* __typeinfo() const; \
public:
/// Matching implement for DECLARE_ABSTRACT_CLASS or DECLARE_CLASS for classes
/// that are not instantiable through the API.
///
/// @code
/// class MyClass : public EngineObject
/// {
/// DECLARE_CLASS( MyClass, EngineObject );
/// DECLARE_INSCOPE( MyScope );
/// DECLARE_NONINSTANTIABLE;
/// };
///
/// IMPLEMENT_NONINSTANTIABLE_CLASS( MyClass, "My class." )
/// PROPERTY( myProperty, 1, "My property.", EnginePropertyTransient )
/// END_IMPLEMENT_CLASS;
/// @endcode
///
/// @note If you want the export name to be different to the actual class
/// name, use a typedef.
///
/// @param type The C++ class type.
/// @doc Documentation string.
#define IMPLEMENT_NONINSTANTIABLE_CLASS( type, doc ) \
DEFINE_CALLIN( fn ## type ## _staticGetType, staticGetType, type, const EngineTypeInfo*, (),,, \
"Get the type info object for the " #type " class.\n\n" \
"@return The type info object for " #type ) \
{ \
return ::TYPE< type >(); \
} \
EngineClassTypeInfo< type::ThisType, type::_ClassBase > \
type::_smTypeInfo( #type, &_SCOPE< __DeclScope >()(), doc ); \
EngineExportScope& type::__engineExportScope() { return type::_smTypeInfo; } \
const EngineTypeInfo* type::__typeinfo() const { return &_smTypeInfo; } \
namespace { namespace _ ## type { \
extern EnginePropertyTable _propertyTable; \
} } \
EnginePropertyTable& type::_smPropertyTable = _ ## type::_propertyTable; \
namespace { namespace _ ## type { \
EnginePropertyTable::Property _properties[] = {
/// Matching implement to DECLARE_CLASS for classes that are instantiable
/// through the API.
///
/// @note engineFunctions.h must be included for this macro to work.
///
/// @param type The C++ class type.
/// @param doc Documentation string.
#define IMPLEMENT_CLASS( type, doc ) \
DEFINE_CALLIN( fn ## type ## _create, create, type, type*, (),,, \
"Create a new " #type " instance.\n" \
"@return A new " #type " instance with a reference count of 1." ) \
{ \
return ::_CREATE< type >(); \
} \
IMPLEMENT_NONINSTANTIABLE_CLASS( type, doc )
/// Close an IMPLEMENT_CLASS or IMPLEMENT_NONINSTANTIABLE_CLASS block.
#define END_IMPLEMENT_CLASS \
{ NULL } \
}; \
EnginePropertyTable _propertyTable \
( sizeof( _properties ) / sizeof( _properties[ 0 ] ) - 1, _properties ); \
} }
/// Define a property on the current class.
///
/// A property named XXX must have a corresponding "getXXX" and/or "setXXX" accessor
/// method defined on the class. If there is only a "setXXX" method, the property is
/// write-only. If there is only a "getXXX", the property is read-only. If both are
/// defined, the property is read-write.
///
/// If the accessors are static methods, the property is a static property. Otherwise
/// it is an instance property.
///
/// The type of the property is determined by its accessor methods.
///
/// A getXXX method must take no arguments and return a value. A setXXX method must take
/// one argument and return void.
///
/// If the property is indexed (@a numElements != 1), the get and set methods take an
/// additional first argument which is the integer index (type S32). Additionally, the get method
/// must be called "getXXXElement" and the set method must be called "setXXXElement".
///
/// Indexed properties may be either fixed-size or variable-sized. Fixed-size indexed
/// properties have numElements count > 1. Variable-size indexed properties have a
/// numElements count of 0. Variable-sized indexed properties must have an additional
/// method "getXXXCount" that returns the current count of elements for the given property.
///
/// @code
/// IMPLEMENT_CLASS( MyClass, "My class." )
/// PROPERTY( myProperty, 1, "My property.", EnginePropertyTransient )
/// END_IMPLEMENT_CLASS;
/// @endcode
///
/// @note Case is ignored when matching get and set methods to property definitions.
///
/// @param name The name of the property. Must correspond with the get and/or set methods.
/// @param numElements If this is a fixed size array property, this is the number of elements
/// the fixed array has. Otherwise 1.
/// @param doc Documentation string.
/// @param flags A combination of EnginePropertyFlags or simply 0 if not applicable.
///
/// @see EnginePropertyFlags
#define PROPERTY( name, numElements, doc, flags ) \
{ #name, doc, numElements, flags },
///
#define PROPERTY_GROUP( name, numElements, doc ) \
{ #name, doc, numElements, EnginePropertyGroupBegin },
///
#define END_PROPERTY_GROUP \
{ NULL, NULL, 0, EnginePropertyGroupEnd },
/// Define a custom create function for this class and its subclasses. Create
/// functions are used to create new instances of class types. This code will be
/// called by the automatically generated "createXXX" functions exported through
/// the API.
///
/// The type of class being created is available to the code as the type parameter "T"
/// which is guaranteed to be a subtype of the current class type.
///
/// @code
/// class MyClass : public EngineObject
/// {
/// DECLARE_CLASS( MyClass, EngineObject );
/// protected:
/// DEFINE_CREATE_METHOD
/// {
/// T* object = new T();
/// object->incRefCount();
/// object->_registerObject();
/// return object;
/// }
/// virtual void _registerObject();
/// };
/// @endcode
///
/// @note A create method must return an object with a reference count of 1.
#define DEFINE_CREATE_METHOD \
template< typename T > \
static T* __create()
/// Define a method that calls back into the control layer.
///
/// @see IMPLEMENT_CALLBACK
#define DECLARE_CALLBACK( returnType, name, args ) \
virtual returnType name ## _callback args
// Our backdoor into calling the __create method on any class even if it is
// protected or private. This function is automatically made friends of any
// EngineObject class. Should be used except by internal API code.
template< typename T >
inline T* _CREATE()
{
return T::template __create< T >();
}
/// Interface for object memory allocation.
class IEngineObjectPool
{
public:
/// Allocate a new object memory block of the given size.
/// @return Pointer to a new memory block or NULL on failure.
virtual void* allocateObject( U32 size TORQUE_TMM_ARGS_DECL ) = 0;
/// Return the member for the object at the given address to the
/// allocator for reuse.
/// @param ptr Pointer to an object memory block previously allocated with allocateObject().
virtual void freeObject( void* ptr ) = 0;
/// Instance of the object pool to use by default.
static IEngineObjectPool* DEFAULT;
};
/// Singleton class that uses the C runtime memory routines for allocating objects.
class EngineCRuntimeObjectPool : public IEngineObjectPool
{
public:
typedef IEngineObjectPool Parent;
protected:
static EngineCRuntimeObjectPool smInstance;
public:
/// Return the singleton instance of this pool.
static EngineCRuntimeObjectPool* instance() { return &smInstance; }
// IEngineObjectPool
virtual void* allocateObject( U32 size TORQUE_TMM_ARGS_DECL );
virtual void freeObject( void* ptr );
};
/// Base class for all objects that may be passed to the control layer.
///
/// A set of rules applies to all EngineObject-derived classes:
///
/// - Every EngineObject class must have a default constructor.
/// - The default constructor and the destructor of every EngineObject class must be public.
/// - If an EngineObject class inherits from multiple classes, the class leading back to EngineObject
/// must be the @b first class in the list to ensure binary-compatible class layouts.
/// - EngineObjects are cooperatively reference-counted by both the engine as well as the control
/// layer.
class EngineObject : public StrongRefBase
{
public:
DECLARE_ABSTRACT_CLASS( EngineObject, void );
DECLARE_INSCOPE( _GLOBALSCOPE );
DECLARE_INSTANTIABLE;
friend const EngineTypeInfo* TYPEOF( const EngineObject* ); // __typeinfo
friend void*& _USERDATA( EngineObject* ); // mEngineObjectUserData
friend class StaticEngineObject; // mEngineObjectPool
protected:
typedef ::FalseType __IsDisposableType;
typedef ::FalseType __IsSingletonType;
DEFINE_CREATE_METHOD
{
T* object = new T;
object->incRefCount();
return object;
}
/// Subclasses should overload this method instead of the public destroySelf().
virtual void _destroySelf() {}
///
static void* _allocateObject( size_t size, IEngineObjectPool* pool TORQUE_TMM_ARGS_DECL );
public:
EngineObject();
virtual ~EngineObject();
/// Return a string that describes this instance. Meant primarily for debugging.
virtual String describeSelf() const;
#ifndef TORQUE_DISABLE_MEMORY_MANAGER
// Make sure no matter what, we get the new/delete calls.
void* operator new( size_t size );
void* operator new( size_t size, IEngineObjectPool* pool );
#endif
/// Allocate a new object in the default object pool.
/// @param size Size of the object in bytes.
/// @return Memory block for new object; never NULL.
void* operator new( size_t size TORQUE_TMM_ARGS_DECL );
/// Allocate a new object in the given object pool.
///
/// If the given pool's allocateObject returns NULL, the method will fall back
/// to the default pool.
///
/// @param size Size of the object in bytes.
/// @param pool Object pool to allocate the object in.
/// @return Memory block for the new object; never NULL.
void* operator new( size_t size, IEngineObjectPool* pool TORQUE_TMM_ARGS_DECL );
/// Placement new.
void* operator new( size_t size, void* ptr ) { return ptr; }
/// Release the given object's memory in the pool it has been allocated from.
void operator delete( void* ptr );
/// Return the pool of EngineObjects to which this object belongs.
IEngineObjectPool* getEngineObjectPool() const { return mEngineObjectPool; }
// StrongRefBase
virtual void destroySelf();
#ifdef TORQUE_DEBUG
/// @name Instance Tracking (debugging only)
///
/// In debug builds, all EngineObjects are kept on a global list so that it is easy
/// to enumerate all live objects at any time.
///
/// @note This is @b NOT thread-safe.
/// @{
/// Type of callback function for iterating over EngineObject instances.
typedef void ( *DebugEnumInstancesCallback )( EngineObject* );
/// Dump describeSelf()s of all live ConsoleObjects to the console.
static void debugDumpInstances();
/// Call the given callback for all instances of the given type.
/// Callback may also be NULL in which case the method just iterates
/// over all instances of the given type. This is useful for setting
/// a breakpoint during debugging.
static void debugEnumInstances( const std::type_info& type, DebugEnumInstancesCallback callback );
/// Same as above but uses an export class name and also includes
/// inheritance (i.e. enumerates all instances of the given class and
/// its subclasses).
static void debugEnumInstances( const char* className, DebugEnumInstancesCallback callback );
private:
/// Next object in global link chain of engine objects.
/// @note Debug builds only.
EngineObject* mNextEngineObject;
/// Previous object in global link chain of engine objects.
/// @note Debug builds only.
EngineObject* mPrevEngineObject;
/// Total number of engine objects currently instantiated.
/// @note Debug builds only.
static U32 smNumEngineObjects;
/// First object in the global link chain of engine objects.
/// @note Debug builds only.
static EngineObject* smFirstEngineObject;
/// @}
#endif
private:
/// Object pool to which this object belongs. If this is NULL,
/// the object will not deallocate itself when it is destructed.
/// This is useful for inline allocation of objects.
IEngineObjectPool* mEngineObjectPool;
/// Opaque user data pointer that the control layer may install
/// on any engine object. Most importantly, this allows control layers
/// to very easily keep track of EngineObjects that they have already
/// created their own wrapper objects for.
void* mEngineObjectUserData;
// Disable array new/delete operators.
void* operator new[]( size_t );
void operator delete[]( void* );
};
/// A statically allocated engine object.
///
/// Static objects have an implicit initial reference count of one and will not
/// delete themselves even when asked to do so.
class StaticEngineObject : public EngineObject
{
public:
DECLARE_ABSTRACT_CLASS( StaticEngineObject, EngineObject );
DECLARE_NONINSTANTIABLE;
StaticEngineObject();
// EngineObject.
virtual void destroySelf();
};
typedef StrongRefPtr< EngineObject > EngineObjectRef;
typedef WeakRefPtr< EngineObject > EngineObjectWeakRef;
/// Return the type info object for the dynamic type of the given object.
/// @param object An EngineObject or NULL.
/// @return An EngineTypeInfo instance or NULL if @a object is NULL.
inline const EngineTypeInfo* TYPEOF( const EngineObject* object )
{
if( !object )
return NULL;
return object->__typeinfo();
}
#include "platform/tmm_on.h"
#endif // !_ENGINEOBJECT_H_