[HL25] Backport interface code changes

This commit is contained in:
Joël Troch 2024-08-28 18:20:40 +02:00
parent 203f7d86fa
commit 0f7d154d31
3 changed files with 243 additions and 5 deletions

View file

@ -61,7 +61,7 @@ InterfaceReg::InterfaceReg(InstantiateInterfaceFn fn, const char* pName) : m_pNa
// ------------------------------------------------------------------------------------ //
// CreateInterface.
// ------------------------------------------------------------------------------------ //
DLLEXPORT IBaseInterface* CreateInterface(const char* pName, int* pReturnCode)
EXPORT_FUNCTION void* CreateInterface(const char* pName, int* pReturnCode)
{
InterfaceReg* pCur;
@ -84,6 +84,8 @@ DLLEXPORT IBaseInterface* CreateInterface(const char* pName, int* pReturnCode)
return NULL;
}
// BEN-NOTE: unifying this on all platforms
#if 0
//Local version of CreateInterface, marked static so that it is never merged with the version in other libraries
static IBaseInterface* CreateInterfaceLocal(const char* pName, int* pReturnCode)
{
@ -107,6 +109,7 @@ static IBaseInterface* CreateInterfaceLocal(const char* pName, int* pReturnCode)
}
return NULL;
}
#endif // 0
//-----------------------------------------------------------------------------
// Purpose: returns a pointer to a function, given a module
@ -220,5 +223,10 @@ CreateInterfaceFn Sys_GetFactory(CSysModule* pModule)
//-----------------------------------------------------------------------------
CreateInterfaceFn Sys_GetFactoryThis()
{
// BEN-NOTE: unifying this on all platforms
#if 0
return CreateInterfaceLocal;
#else
return CreateInterface;
#endif
}

View file

@ -14,9 +14,12 @@
// 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.
//#if _MSC_VER >= 1300 // VC7
//#include "tier1/interface.h"
//#else
// JoshA: Everything uses this now.
#if 1
#include "tier1/interface.h"
#else
#error use proper interface only
#pragma once
@ -79,9 +82,18 @@ public:
static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName);
// 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
// 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
@ -113,4 +125,4 @@ extern void Sys_UnloadModule(CSysModule* pModule);
extern CreateInterfaceFn Sys_GetFactory(CSysModule* pModule);
//#endif // MSVC 6.0
#endif // MSVC 6.0

218
public/tier1/interface.h Normal file
View file

@ -0,0 +1,218 @@
//========= Copyright <20> 1996-2005, 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 HMODULE void *
#define GetProcAddress dlsym
#define _snprintf snprintf
#endif
// All interfaces derive from this.
class IBaseInterface
{
public:
virtual ~IBaseInterface() {}
};
#define CREATEINTERFACE_PROCNAME "CreateInterface"
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;
};
#if defined(_STATIC_LINKED) && defined(_SUBSYSTEM)
#endif
// 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 (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 (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(className, interfaceName, versionName, globalVarName) \
static void* __Create##className##interfaceName##_interface() {return (interfaceName *)&globalVarName;} \
static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName);
#else
#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \
namespace _SUBSYSTEM \
{ \
static void* __Create##className##interfaceName##_interface() {return (interfaceName *)&globalVarName;} \
static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); \
}
#endif
// 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
// 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
// extend this for other error conditions/code
// load/unload components
class CSysModule;
// interface return status
enum
{
IFACE_OK = 0,
IFACE_FAILED
};
#if defined(_STATIC_LINKED) && defined(_SUBSYSTEM)
#endif
#ifdef WIN32
#define EXPORT_FUNCTION __declspec(dllexport)
#else
#define EXPORT_FUNCTION __attribute__ ((visibility("default")))
#endif
#ifndef DLL_EXPORT
#define DLL_EXPORT EXPORT_FUNCTION
#endif
//-----------------------------------------------------------------------------
// 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
//-----------------------------------------------------------------------------
extern "C" DLL_EXPORT void* CreateInterface(const char *pName, int *pReturnCode);
extern CreateInterfaceFn Sys_GetFactoryThis( void );
//-----------------------------------------------------------------------------
// UNDONE: This is obsolete, use the module load/unload/get instead!!!
//-----------------------------------------------------------------------------
extern CreateInterfaceFn Sys_GetFactory( const char *pModuleName );
struct ModuleCount_t
{
CSysModule *m_pSysModule;
int m_cRef;
bool operator==( const ModuleCount_t &rhs ) const { return rhs.m_pSysModule == m_pSysModule; }
};
//-----------------------------------------------------------------------------
// 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 );
extern void Sys_UnloadModule( CSysModule *pModule );
extern CreateInterfaceFn Sys_GetFactory( 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 );
#endif