You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
9.0 KiB
230 lines
9.0 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
|
|
// This header defines the interface convention used in the valve engine. |
|
// To make an interface and expose it: |
|
// 1. The interface must be ALL pure virtuals, and have no data members. |
|
// 2. Define a name for it. |
|
// 3. In its implementation file, use EXPOSE_INTERFACE or EXPOSE_SINGLE_INTERFACE. |
|
|
|
// Versioning |
|
// There are two versioning cases that are handled by this: |
|
// 1. You add functions to the end of an interface, so it is binary compatible with the previous interface. In this case, |
|
// you need two EXPOSE_INTERFACEs: one to expose your class as the old interface and one to expose it as the new interface. |
|
// 2. You update an interface so it's not compatible anymore (but you still want to be able to expose the old interface |
|
// for legacy code). In this case, you need to make a new version name for your new interface, and make a wrapper interface and |
|
// expose it for the old interface. |
|
|
|
// Static Linking: |
|
// Must mimic unique seperate class 'InterfaceReg' constructors per subsystem. |
|
// Each subsystem can then import and export interfaces as expected. |
|
// This is achieved through unique namespacing 'InterfaceReg' via symbol _SUBSYSTEM. |
|
// Static Linking also needs to generate unique symbols per interface so as to |
|
// provide a 'stitching' method whereby these interface symbols can be referenced |
|
// via the lib's primary module (usually the lib's interface exposure) |
|
// therby stitching all of that lib's code/data together for eventual final exe link inclusion. |
|
|
|
#ifndef INTERFACE_H |
|
#define INTERFACE_H |
|
|
|
#ifdef _WIN32 |
|
#pragma once |
|
#endif |
|
|
|
#ifdef _LINUX |
|
#include <dlfcn.h> // dlopen,dlclose, et al |
|
#include <unistd.h> |
|
|
|
#define GetProcAddress dlsym |
|
|
|
#ifdef _snprintf |
|
#undef _snprintf |
|
#endif |
|
#define _snprintf snprintf |
|
#endif |
|
|
|
// TODO: move interface.cpp into tier0 library. |
|
#include "tier0/platform.h" |
|
|
|
// All interfaces derive from this. |
|
class IBaseInterface |
|
{ |
|
public: |
|
virtual ~IBaseInterface() {} |
|
}; |
|
|
|
#if !defined( _X360 ) |
|
#define CREATEINTERFACE_PROCNAME "CreateInterface" |
|
#else |
|
// x360 only allows ordinal exports, .def files export "CreateInterface" at 1 |
|
#define CREATEINTERFACE_PROCNAME ((const char*)1) |
|
#endif |
|
|
|
typedef void* (*CreateInterfaceFn)(const char *pName, int *pReturnCode); |
|
typedef void* (*InstantiateInterfaceFn)(); |
|
|
|
// Used internally to register classes. |
|
class InterfaceReg |
|
{ |
|
public: |
|
InterfaceReg(InstantiateInterfaceFn fn, const char *pName); |
|
|
|
public: |
|
InstantiateInterfaceFn m_CreateFn; |
|
const char *m_pName; |
|
|
|
InterfaceReg *m_pNext; // For the global list. |
|
static InterfaceReg *s_pInterfaceRegs; |
|
}; |
|
|
|
// Use this to expose an interface that can have multiple instances. |
|
// e.g.: |
|
// EXPOSE_INTERFACE( CInterfaceImp, IInterface, "MyInterface001" ) |
|
// This will expose a class called CInterfaceImp that implements IInterface (a pure class) |
|
// clients can receive a pointer to this class by calling CreateInterface( "MyInterface001" ) |
|
// |
|
// In practice, the shared header file defines the interface (IInterface) and version name ("MyInterface001") |
|
// so that each component can use these names/vtables to communicate |
|
// |
|
// A single class can support multiple interfaces through multiple inheritance |
|
// |
|
// Use this if you want to write the factory function. |
|
#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) |
|
#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName) \ |
|
static InterfaceReg __g_Create##interfaceName##_reg(functionName, versionName); |
|
#else |
|
#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName) \ |
|
namespace _SUBSYSTEM \ |
|
{ \ |
|
static InterfaceReg __g_Create##interfaceName##_reg(functionName, versionName); \ |
|
} |
|
#endif |
|
|
|
#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) |
|
#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ |
|
static void* __Create##className##_interface() {return static_cast<interfaceName *>( new className );} \ |
|
static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName ); |
|
#else |
|
#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ |
|
namespace _SUBSYSTEM \ |
|
{ \ |
|
static void* __Create##className##_interface() {return static_cast<interfaceName *>( new className );} \ |
|
static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName ); \ |
|
} |
|
#endif |
|
|
|
// Use this to expose a singleton interface with a global variable you've created. |
|
#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) |
|
#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR_WITH_NAMESPACE(className, interfaceNamespace, interfaceName, versionName, globalVarName) \ |
|
static void* __Create##className##interfaceName##_interface() {return static_cast<interfaceNamespace interfaceName *>( &globalVarName );} \ |
|
static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); |
|
#else |
|
#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR_WITH_NAMESPACE(className, interfaceNamespace, interfaceName, versionName, globalVarName) \ |
|
namespace _SUBSYSTEM \ |
|
{ \ |
|
static void* __Create##className##interfaceName##_interface() {return static_cast<interfaceNamespace interfaceName *>( &globalVarName );} \ |
|
static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); \ |
|
} |
|
#endif |
|
|
|
#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR_WITH_NAMESPACE(className, , interfaceName, versionName, globalVarName) |
|
|
|
// Use this to expose a singleton interface. This creates the global variable for you automatically. |
|
#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) |
|
#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ |
|
static className __g_##className##_singleton; \ |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) |
|
#else |
|
#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ |
|
namespace _SUBSYSTEM \ |
|
{ \ |
|
static className __g_##className##_singleton; \ |
|
} \ |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) |
|
#endif |
|
|
|
// load/unload components |
|
class CSysModule; |
|
|
|
// interface return status |
|
enum |
|
{ |
|
IFACE_OK = 0, |
|
IFACE_FAILED |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// This function is automatically exported and allows you to access any interfaces exposed with the above macros. |
|
// if pReturnCode is set, it will return one of the following values (IFACE_OK, IFACE_FAILED) |
|
// extend this for other error conditions/code |
|
//----------------------------------------------------------------------------- |
|
DLL_EXPORT void* CreateInterface(const char *pName, int *pReturnCode); |
|
|
|
#if defined( _X360 ) |
|
DLL_EXPORT void *CreateInterfaceThunk( const char *pName, int *pReturnCode ); |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// UNDONE: This is obsolete, use the module load/unload/get instead!!! |
|
//----------------------------------------------------------------------------- |
|
extern CreateInterfaceFn Sys_GetFactory( CSysModule *pModule ); |
|
extern CreateInterfaceFn Sys_GetFactory( const char *pModuleName ); |
|
extern CreateInterfaceFn Sys_GetFactoryThis( void ); |
|
|
|
enum Sys_Flags |
|
{ |
|
SYS_NOFLAGS = 0x00, |
|
SYS_NOLOAD = 0x01 // no loading, no ref-counting, only returns handle if lib is loaded. |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Load & Unload should be called in exactly one place for each module |
|
// The factory for that module should be passed on to dependent components for |
|
// proper versioning. |
|
//----------------------------------------------------------------------------- |
|
extern CSysModule *Sys_LoadModule( const char *pModuleName, Sys_Flags flags = SYS_NOFLAGS ); |
|
extern void Sys_UnloadModule( CSysModule *pModule ); |
|
|
|
// This is a helper function to load a module, get its factory, and get a specific interface. |
|
// You are expected to free all of these things. |
|
// Returns false and cleans up if any of the steps fail. |
|
bool Sys_LoadInterface( |
|
const char *pModuleName, |
|
const char *pInterfaceVersionName, |
|
CSysModule **pOutModule, |
|
void **pOutInterface ); |
|
|
|
bool Sys_IsDebuggerPresent(); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Place this as a singleton at module scope (e.g.) and use it to get the factory from the specified module name. |
|
// |
|
// When the singleton goes out of scope (.dll unload if at module scope), |
|
// then it'll call Sys_UnloadModule on the module so that the refcount is decremented |
|
// and the .dll actually can unload from memory. |
|
//----------------------------------------------------------------------------- |
|
class CDllDemandLoader |
|
{ |
|
public: |
|
CDllDemandLoader( char const *pchModuleName ); |
|
virtual ~CDllDemandLoader(); |
|
CreateInterfaceFn GetFactory(); |
|
void Unload(); |
|
|
|
private: |
|
|
|
char const *m_pchModuleName; |
|
CSysModule *m_hModule; |
|
bool m_bLoadAttempted; |
|
}; |
|
|
|
#endif |
|
|
|
|
|
|
|
|