Developer Guide (Version 1.12)

Implementing Extensions Using the Framework

The following section explains in detail how to implement extensions in Lumberyard. It provides examples that use glue code and do not use glue code. The section also shows you how to utilize the framework in cases where ICryUnknown cannot be the base of the extension interface.

The public interface header that will be included by the client should look like the following.

// IMyExtension.h #include <CryExtension/ICryUnknown.h> struct IMyExtension : public ICryUnknown { ... };

If you are using glue code, declare the implementation class of the extension in the header file as follows.

// MyExtension.h #include <IMyExtension.h> #include <CryExtension/Impl/ClassWeaver.h> class CMyExtension : public IMyExtension { ... };

Using Glue Code

The first example shows a possible implementation of the IMyExtension class in the previous examples.

/////////////////////////////////////////// // public section // IMyExtension.h #include <CryExtension/ICryUnknown.h> struct IMyExtension : public ICryUnknown { CRYINTERFACE_DECLARE(IMyExtension, 0x4fb87a5f83f74323, 0xa7e42ca947c549d8) virtual void CallMe() = 0; }; typedef boost::shared_ptr<IMyExtension> IMyExtensionPtr; /////////////////////////////////////////// // private section not visible to client // MyExtension.h #include <IMyExtension.h> #include <CryExtension/Impl/ClassWeaver.h> class CMyExtension : public IMyExtension { CRYINTERFACE_BEGIN() CRYINTERFACE_ADD(IMyExtension) CRYINTERFACE_END() CRYGENERATE_CLASS(CMyExtension, "MyExtension", 0x68c7f0e0c36446fe, 0x82a3bc01b54dc7bf) public: virtual void CallMe(); }; // MyExtension.cpp #include "MyExtension.h" CRYREGISTER_CLASS(CMyExtension) CMyExtension::CMyExtension() { } CMyExtension::~CMyExtension() { } void CMyExtension::CallMe() { printf("Inside CMyExtension::CallMe()..."); }

The following example shows how the extension class MyExtension can be customized and expanded to implement two more interfaces, IFoo and IBar.

/////////////////////////////////////////// // public section // IFoo.h #include <CryExtension/ICryUnknown.h> struct IFoo : public ICryUnknown { CRYINTERFACE_DECLARE(IFoo, 0x7f073239d1e6433f, 0xb59c1b6ff5f68d79) virtual void Foo() = 0; }; // IBar.h #include <CryExtension/ICryUnknown.h> struct IBar : public ICryUnknown { CRYINTERFACE_DECLARE(IBar, 0xa9361937f60d4054, 0xb716cb711970b5d1) virtual void Bar() = 0; }; /////////////////////////////////////////// // private section not visible to client // MyExtensionCustomized.h #include "MyExtension.h" #include <IFoo.h> #include <IBar.h> #include <CryExtension/Impl/ClassWeaver.h> class CMyExtensionCustomized : public CMyExtension, public IFoo, public IBar { CRYINTERFACE_BEGIN() CRYINTERFACE_ADD(IFoo) CRYINTERFACE_ADD(IBar) CRYINTERFACE_ENDWITHBASE(CMyExtension) CRYGENERATE_CLASS(CMyExtensionCustomized, "MyExtensionCustomized", 0x07bfa7c543a64f0c, 0x861e9fa3f7d7d264) public: virtual void CallMe(); // chose to override MyExtension's impl virtual void Foo(); virtual void Bar(); }; // MyExtensionCustomized.cpp #include "MyExtensionCustomized.h" CRYREGISTER_CLASS(CMyExtensionCustomized) CMyExtensionCustomized::CMyExtensionCustomized() { } CMyExtensionCustomized::~CMyExtensionCustomized() { } void CMyExtensionCustomized::CallMe() { printf("Inside CMyExtensionCustomized::CallMe()..."); } void CMyExtensionCustomized::Foo() { printf("Inside CMyExtensionCustomized::Foo()..."); } void CMyExtensionCustomized::Bar() { printf("Inside CMyExtensionCustomized::Bar()..."); }

Without Using Glue Code

If for any reason using the glue code is neither desired nor applicable, extensions can be implemented as follows. It is recommended to implement ICryUnknown and ICryFactory such that their runtime cost is equal to the one provided by the glue code. For more information, see ICryUnknown and ICryFactory.

/////////////////////////////////////////// // public section // INoMacros.h #include <CryExtension/ICryUnknown.h> struct INoMacros : public ICryUnknown { // befriend cryiidof and boost::checked_delete template <class T> friend const CryInterfaceID& InterfaceCastSemantics::cryiidof(); template <class T> friend void boost::checked_delete(T* x); protected: virtual ~INoMacros() {} private: // It's very important that this static function is implemented for each interface! // Otherwise the consistency of cryinterface_cast<T>() is compromised because // cryiidof<T>() = cryiidof<baseof<T>>() {baseof<T> = ICryUnknown in most cases} static const CryInterfaceID& IID() { static const CryInterfaceID iid = {0xd0fda1427dee4cceull, 0x88ff91b6b7be2a1full}; return iid; } public: virtual void TellMeWhyIDontLikeMacros() = 0; }; typedef boost::shared_ptr<INoMacros> INoMacrosPtr; /////////////////////////////////////////// // private section not visible to client // NoMacros.cpp // // This is just an exemplary implementation! // For brevity the whole implementation is packed into this cpp file. #include <INoMacros.h> #include <CryExtension/ICryFactory.h> #include <CryExtension/Impl/RegFactoryNode.h> // implement factory first class CNoMacrosFactory : public ICryFactory { // ICryFactory public: virtual const char* GetClassName() const { return "NoMacros"; } virtual const CryClassID& GetClassID() const { static const CryClassID cid = {0xa4550317690145c1ull, 0xa7eb5d85403dfad4ull}; return cid; } virtual bool ClassSupports(const CryInterfaceID& iid) const { return iid == cryiidof<ICryUnknown>() || iid == cryiidof<INoMacros>(); } virtual void ClassSupports(const CryInterfaceID*& pIIDs, size_t& numIIDs) const { static const CryInterfaceID iids[2] = {cryiidof<ICryUnknown>(), cryiidof<INoMacros>()}; pIIDs = iids; numIIDs = 2; } virtual ICryUnknownPtr CreateClassInstance() const; public: static CNoMacrosFactory& Access() { return s_factory; } private: CNoMacrosFactory() {} ~CNoMacrosFactory() {} private: static CNoMacrosFactory s_factory; }; CNoMacrosFactory CNoMacrosFactory::s_factory; // implement extension class class CNoMacros : public INoMacros { // ICryUnknown public: virtual ICryFactory* GetFactory() const { return &CNoMacrosFactory::Access(); }; // befriend boost::checked_delete // only needed to be able to create initial shared_ptr<CNoMacros> // so we don't lose type info for debugging (i.e. inspecting shared_ptr) template <class T> friend void boost::checked_delete(T* x); protected: virtual void* QueryInterface(const CryInterfaceID& iid) const { if (iid == cryiidof<ICryUnknown>()) return (void*) (ICryUnknown*) this; else if (iid == cryiidof<INoMacros>()) return (void*) (INoMacros*) this; else return 0; } virtual void* QueryComposite(const char* name) const { return 0; } // INoMacros public: virtual void TellMeWhyIDontLikeMacros() { printf("Woohoo, no macros...\n"); } CNoMacros() {} protected: virtual ~CNoMacros() {} }; // implement factory's CreateClassInstance method now that extension class is fully visible to compiler ICryUnknownPtr CNoMacrosFactory::CreateClassInstance() const { boost::shared_ptr<CNoMacros> p(new CDontLikeMacros); return ICryUnknownPtr(*static_cast<boost::shared_ptr<ICryUnknown>*>(static_cast<void*>(&p))); } // register extension static SRegFactoryNode g_noMacrosFactory(&CNoMacrosFactory::Access());

Exposing Composites

The following example shows how to expose (inherited) composites. For brevity, the sample is not separated into files.

////////////////////////////////////////////////////////////////////////// struct ITestExt1 : public ICryUnknown { CRYINTERFACE_DECLARE(ITestExt1, 0x9d9e0dcfa5764cb0, 0xa73701595f75bd32) virtual void Call1() = 0; }; typedef boost::shared_ptr<ITestExt1> ITestExt1Ptr; class CTestExt1 : public ITestExt1 { CRYINTERFACE_BEGIN() CRYINTERFACE_ADD(ITestExt1) CRYINTERFACE_END() CRYGENERATE_CLASS(CTestExt1, "TestExt1", 0x43b04e7cc1be45ca, 0x9df6ccb1c0dc1ad8) public: virtual void Call1(); }; CRYREGISTER_CLASS(CTestExt1) CTestExt1::CTestExt1() { } CTestExt1::~CTestExt1() { } void CTestExt1::Call1() { } ////////////////////////////////////////////////////////////////////////// class CComposed : public ICryUnknown { CRYINTERFACE_BEGIN() CRYINTERFACE_END() CRYCOMPOSITE_BEGIN() CRYCOMPOSITE_ADD(m_pTestExt1, "Ext1") CRYCOMPOSITE_END(CComposed) CRYGENERATE_CLASS(CComposed, "Composed", 0x0439d74b8dcd4b7f, 0x9287dcdf7e26a3a5) private: ITestExt1Ptr m_pTestExt1; }; CRYREGISTER_CLASS(CComposed) CComposed::CComposed() : m_pTestExt1() { CryCreateClassInstance("TestExt1", m_pTestExt1); } CComposed::~CComposed() { } ////////////////////////////////////////////////////////////////////////// struct ITestExt2 : public ICryUnknown { CRYINTERFACE_DECLARE(ITestExt2, 0x8eb7a4b399874b9c, 0xb96bd6da7a8c72f9) virtual void Call2() = 0; }; DECLARE_BOOST_POINTERS(ITestExt2); class CTestExt2 : public ITestExt2 { CRYINTERFACE_BEGIN() CRYINTERFACE_ADD(ITestExt2) CRYINTERFACE_END() CRYGENERATE_CLASS(CTestExt2, "TestExt2", 0x25b3ebf8f1754b9a, 0xb5494e3da7cdd80f) public: virtual void Call2(); }; CRYREGISTER_CLASS(CTestExt2) CTestExt2::CTestExt2() { } CTestExt2::~CTestExt2() { } void CTestExt2::Call2() { } ////////////////////////////////////////////////////////////////////////// class CMultiComposed : public CComposed { CRYCOMPOSITE_BEGIN() CRYCOMPOSITE_ADD(m_pTestExt2, "Ext2") CRYCOMPOSITE_ENDWITHBASE(CMultiComposed, CComposed) CRYGENERATE_CLASS(CMultiComposed, "MultiComposed", 0x0419d74b8dcd4b7e, 0x9287dcdf7e26a3a6) private: ITestExt2Ptr m_pTestExt2; }; CRYREGISTER_CLASS(CMultiComposed) CMultiComposed::CMultiComposed() : m_pTestExt2() { CryCreateClassInstance("TestExt2", m_pTestExt2); } CMultiComposed::~CMultiComposed() { } ... ////////////////////////////////////////////////////////////////////////// // let's use it ICryUnknownPtr p; if (CryCreateClassInstance("MultiComposed", p)) { ITestExt1Ptr p1 = cryinterface_cast<ITestExt1>(crycomposite_query(p, "Ext1")); if (p1) p1->Call1(); // calls CTestExt1::Call1() ITestExt2Ptr p2 = cryinterface_cast<ITestExt2>(crycomposite_query(p, "Ext2")); if (p2) p2->Call2(); // calls CTestExt2::Call2() }

If ICryUnknown Cannot Be the Base of the Extension Class

There are cases where making ICryUnknown the base of your extension class is not possible. Some examples are legacy code bases that cannot be modified, third party code for which you do not have full source code access, or code whose modification is not practical. However, these code bases can provide useful functionality (for example, for video playback or flash playback) if you expose them as engine extensions. The following sample illustrates how an additional level of indirection can expose a third party API.

/////////////////////////////////////////// // public section // IExposeThirdPartyAPI.h #include <CryExtension/ICryUnknown.h> #include <IThirdPartyAPI.h> struct IExposeThirdPartyAPI : public ICryUnknown { CRYINTERFACE_DECLARE(IExposeThirdPartyAPI, 0x804250bbaacf4a5f, 0x90ef0327bb7a0a7f) virtual IThirdPartyAPI* Create() = 0; }; typedef boost::shared_ptr<IExposeThirdPartyAPI> IExposeThirdPartyAPIPtr; /////////////////////////////////////////// // private section not visible to client // Expose3rdPartyAPI.h #include <IExposeThirdPartyAPI.h> #include <CryExtension/Impl/ClassWeaver.h> class CExposeThirdPartyAPI : public IExposeThirdPartyAPI { CRYINTERFACE_BEGIN() CRYINTERFACE_ADD(IExposeThirdPartyAPI) CRYINTERFACE_END() CRYGENERATE_CLASS(CExposeThirdPartyAPI, "ExposeThirdPartyAPI", 0xa93b970b2c434a21, 0x86acfe94d8dae547) public: virtual IThirdPartyAPI* Create(); }; // ExposeThirdPartyAPI.cpp #include "ExposeThirdPartyAPI.h" #include "ThirdPartyAPI.h" CRYREGISTER_CLASS(CExposeThirdPartyAPI) CExposeThirdPartyAPI::CExposeThirdPartyAPI() { } CExposeThirdPartyAPI::~CExposeThirdPartyAPI() { } IThirdPartyAPI* CExposeThirdPartyAPI::Create() { return new CThirdPartyAPI; // CThirdPartyAPI implements IThirdPartyAPI }

Custom Inclusion and Exclusion of Extensions

To enable easy inclusion and exclusion of extensions, Lumberyard provides a global "extension definition" header much like CryCommon/ProjectDefines.h that is automatically included in all modules by means of the platform.h file. To wrap your extension implementation code, you include a #define statement in the extension definition header. To exclude unused extension code from your build, you can also comment out extensions that you are not interested in. Interface headers are not affected by the #if defined statements, so the client code compiles as is with or without them.

/////////////////////////////////////////// // public section // IMyExtension.h #include <CryExtension/ICryUnknown.h> struct IMyExtension : public ICryUnknown { ... }; typedef boost::shared_ptr<IMyExtension> IMyExtensionPtr; // ExtensionDefines.h ... #define INCLUDE_MYEXTENSION ... /////////////////////////////////////////// // private section not visible to client // MyExtension.h #if defined(INCLUDE_MYEXTENSION) #include <IMyExtension.h> #include <CryExtension/Impl/ClassWeaver.h> class CMyExtension : public IMyExtension { ... }; #endif // #if defined(INCLUDE_MYEXTENSION) // MyExtension.cpp #if defined(INCLUDE_MYEXTENSION) #include "MyExtension.h" CRYREGISTER_CLASS(CMyExtension) ... #endif // #if defined(INCLUDE_MYEXTENSION)

Because extensions can be removed from a build, clients must write their code in a way that does not assume the availability of an extension. For more information, see Using Extensions.