CSEP: 10
Title: Templated implementation of SCF
Version: $Revision: 21828 $
Last-changed: $Date: 2005-07-23 19:41:13 +0100 (Sat, 23 Jul 2005) $
Author: Marten Svanfeldt, Michael D. Adams
Status: Draft
Type: Proposal
Content-Type: text/x-rst
Created: 2005-07-17


Abstract
========
The SCF [1]_ system in CS was designed a few years back to provide a 
lightweight component system resembling the COM system. Due to compiler
limitations at the time of writing and the will of those developers who 
wrote it, it is largely based on macros to help the end-developer utilize 
it. 

Today many find this design both hard to understand and non-modern. 
This CSEP proposes an implementation of SCF based on the support modern
compilers have for templates. 
It does this, while maintaining source compatibility with the current implementation.

If you feel that you know the current SCF system and the motives behind it
you can skip some/large parts of the first sections.

Motivation
==========
The main motivation for a change this big is ease of use and understanding. 
While the macro-system hides a lot of the implementational details from
both the developer implementing new classes and end-developers using them,
they also introduce new problems.

The macros build up new classes, template specializations and function 
implementations at compile time in the preprocessor. While this works well
it results in sometimes very complicated error messages, as the preprocessor
does no error checking, and makes the code hard to trace in a source-following
debugger like MSVC as it shows the macro instead of the code it generates.

The solution to this problem which has been discussed before is to rewrite
the system to instead use class templates. There have been a few proposals 
to the cs-main mailing list over a period of time, but none of these proposals
have reached any generally accepted result. This proposal 
tries to combine ideas with these with solutions that make sure that:

- Source compatibility is maintained. Old code on both creator and user 
  side should work.

- Any runtime and memory usage increase is kept to a minimum. Compile time 
  disturbance should also be considered.

- All features of the current SCF system must remain. You must also be able to
  freely mix code using the old and the new syntax with minimum of limitations.

- The system should be easier to understand, use and debug than the current system.

The authors believe that this proposal will meet all those points.


Technical specification
=======================
Some non-vital parts of the current SCF system are not covered in this proposal 
such as the RefTracking debugging system. This system should be easy to incorporate
once the changes to the main SCF infrastructure is done.

Helper functions
----------------
The following macros will be replaced by templated functions. The macros will be 
implemented in terms of their function equivalent.

====================================================  ========================================
Macro                                                  Template equivalent
====================================================  ========================================
SCF_CREATE_INSTANCE(ClassID,Interface)                scfCreateInstance<Interface>(ClassID)
SCF_QUERY_INTERFACE(Object,Interface)                 scfQueryInterface<Interface>(Object)
SCF_QUERY_INTERFACE_SAFE(Object,Interface)            scfQueryInterfaceSafe<Interface>(Object)
CS_QUERY_REGISTRY(Reg,Interface)                      csQueryRegistry<Interface>(Reg)
CS_QUERY_REGISTRY_TAG(Reg, Tag)                       csQueryRegistryTag(Reg, Tag)
CS_QUERY_REGISTRY_TAG_INTERFACE(Reg, Tag, Interface)  csQueryRegistryTag<Interface>(Reg, Tag)
====================================================  ========================================

Technically the registry macros/functions are not part of SCF, but they are still related.

There will still have to be one macro, the SCF_INTERFACE as this needs the textification
preprocessor directive ``#``

Interface declaration
---------------------
With current SCF an interface is declared as:

::

    SCF_VERSION(iFoo,0,0,1);
    struct iFoo : public iBase
    {
    public:
      virtual void DoFoo ()=0;
    };
    
SCF_VERSION provides a specialization of the class scfInterfaceTrait<typename T>.
With a new system all information about iFoo should be contained within the interface
itself (see rationale) which leads to a similar design:

::

    struct iFoo : public virtual iBase
    public:
      SCF_INTERFACE(iFoo,0,0,1);
      virtual void DoFoo ()=0;
    };


The macro ``SCF_INTERFACE(InterfaceName,Major,Minior,Revision)`` adds two static
methods to the interface, ``const char * GetInterfaceName ()`` and ``int GetInterfaceVersion ()``.


Implementational classes
------------------------
Today the major SCF macro-goop is when a class implements one or several SCF interfaces.
Instead of the macros, by having a templated base-class with template mixins you can provide
the same functionality while having a simpler syntax.


Class implementing a single interface
'''''''''''''''''''''''''''''''''''''
The simplest example is an implementation class implementing a single SCF interface.
Using the new syntax, a class implementing iFoo above would be written:

::

    class csFoo : public scfImplementation1<csFoo, iFoo>
    {
    public:
      csFoo ()
        : scfImplementationType (this)
      {}
      
      virtual ~csFoo()
      {}
    
      void DoFoo ()
      {..}
    };
	

Class implementing two interfaces
'''''''''''''''''''''''''''''''''
Having a class implementing two interfaces is very similar to implementing a single
interface. As this results in multiple inheritance of iBase the interfaces must
derive virtually from iBase. This is an incompatibility between the old and new 
system. Any class that wishes to implement several interfaces with the new system
needs to make sure these classes follow this.

Example of class implementing two interfaces:

::

    class csFoo2 : public scfImplementation2<csFoo, iFoo, iBar>
    {
    public:
      csFoo ()
        : scfImplementationType (this)
      {}
      
      virtual ~csFoo()
      {}
    
      // Implementation of iFoo
      void DoFoo ()
      {..}
      
      // Implementation of iBar
      void DoBar (int a)
      {..}
    };
	

Class extending an existing implementation
''''''''''''''''''''''''''''''''''''''''''
One important feature of SCF is the ability to add new interfaces to an existing
implementation by implementing an extending class. Using the new syntax this is done
as:

::

    class csFooBar : public scfImplementationExt1<csFooBar, csFoo, iBar>
    {
    public:
      csFooBar ()
        : scfImplementationType (this)
      {}
      
      virtual ~csFooBar()
      {}
    
      void DoBar ()
      {..}
    };
	
Embedded interfaces
'''''''''''''''''''
With todays SCF system there is a concept of embedded interfaces where one class
expose an interface but implements it in a contained class. This is a work-around
to avoid multiple inheritance. In most cases where this is used the contained
class only calls the parent-class which results in the containment being just an
extra step.
The new SCF system does already take advantage of multiple inheritance and the 
support for embedded interfaces is therefore dropped.


Rationale
=========

General design
--------------
The whole design of a new SCF system needs to be compatible. This puts some
restrictions on what it can do. The identified restrictions are:

- iBase interface must still contain the same methods. Something to consider
  for the future is to remove the static QueryInterfaceSafe and only have it 
  as a free function. This is to make iBase truly an ABC [2]_.
  
- The reference counting semantics cannot change. It still needs to be
  internal and use intrusive reference counting.
  
- The changes to classes using SCF and providing SCF interfaces should be as
  small as possible. It should be possible to continue to use the old macros
  without breaking anything.
  
Apart from these compatibility imposed restrictions, we've imposed these 
restrictions ourselves on the design, as we believe these makes the system
more usable:

- It should be possible to declare interfaces and implementation classes in
  namespaces. This leads to a secondary requirement that an interface or
  implementation class should not need a template specialization as template
  specialization needs to be in the same namespace as the original template.

- The implementation should not use RTTI as it is problematic over module
  boundaries.

Templated free functions
------------------------
Templated free functions are used instead of macros as the former are type-
safe and is what is recommended by the C++ standard [3]_. The technical reason
to have them is merly convenience and compatibility. The functionality they
provide exists within iBase and iSCF.


scfInterfaceTrait template class
--------------------------------
The scfInterfaceTrait provides a way for SCF to access information about the
interfaces. For compatibility reasons this class is used in two ways:

1. Old-style interfaces. When using the old-style macro SCF_VERSION(..) a
   template specialization of the class is declared. This introduces the 
   limitation that old-style interfaces cannot be declared in namespaces.

2. New-style interfaces. New-style interfaces should work well together
   with namespaces which means the specialization-technique isn't useable.
   Instead the non-specialized version of scfInterfaceTrait<Class> is used 
   and gets information from two static methods in Class named GetInterfaceName 
   and GetInterfaceVersion. 


scfImplementation and scfImplementationN template class
-------------------------------------------------------
scfImplementation and it's derived class scfImplementationN are the classes
providing the main functionality for handling reference counting and querying
interfaces. It is split up into one part for class specific code, 
scfImplementation, and one part which also depends on the number of interfaces 
in use.

The scfImplementationN (N=1->10) classes needs to be explicitly declared
as C++ does not support variable number of template parameters.
Two alternative implementations were considered. The first one use a fixed
number of template arguments and all but the first one defaults to a "null"-
type with a for the usage specialized trait. This however have the disadvantage
that an unsuccessful QueryInterface results in up to nine uneccesary compares
and possibly function calls.
The second alternative is to use paritial template specialization to specialize
types with 1 to 10 real template arguments. While this is a very visually 
pleasing and performant solution it has the great disadvantage of bad compiler
compatbility. Only the very latest compilers have proper support for paritial
specialization.





Reference implementation
========================

Macros
------

::

    #define SCF_CREATE_INSTANCE(ClassID,Interface) \
      scfCreateInstance<Interface>(ClassID)
    #define SCF_QUERY_INTERFACE(Object,Interface) \
      scfQueryInterface<Interface>(Object)
    #define SCF_QUERY_INTERFACE_SAFE(Object,Interface) \
      scfQueryInterfaceSafe<Interface>(Object)
     
    #define CS_QUERY_REGISTRY(Reg,Interface) \
      csQueryRegistry<Interface>(Reg)
    #define CS_QUERY_REGISTRY_TAG(Reg, Tag) \
      csQueryRegistryTag(Reg, Tag)
    #define CS_QUERY_REGISTRY_TAG_INTERFACE(Reg, Tag, Interface) \
      csQueryRegistryTag<Interface>(Reg, Tag)

::

    #define SCF_INTERFACE(InterfaceName,Major,Minior,Micro) \
      struct InterfaceTrait { \
        static int GetInterfaceVersion() { return SCF_CONSTRUCT_VERSION(0, 0, 1); } \
        static char const* GetInterfaceName() {  return #InterfaceName; } \
      };



Helper functions
----------------

::

    template<class Interface>
    csPtr<Interface> scfCreateInstance (char const * const ClassID)
    {
      Interface *x = (Interface *)iSCF::SCF->CreateInstance (ClassID,
                                  scfInterfaceTrait<Interface>::GetName (),
                                  scfInterfaceTrait<Interface>::GetVersion ());
      return csPtr<Interface>(x);
    }
    
::

    template<class Interface, class ClassPtr>
    csPtr<Interface> scfQueryInterface (ClassPtr object)
    {
      Interface *x = (Interface*)object->QueryInterface (
                                 scfInterfaceTrait<Interface>::GetID (),
                                 scfInterfaceTrait<Interface>::GetVersion ());
      return csPtr<Interface> (x);
    }
    
::

    template<class Interface, class ClassPtr>
    csPtr<Interface> scfQueryInterfaceSafe (ClassPtr object)
    {
      if (object == 0) return csPtr<Interface> (0);
      
      Interface *x = (Interface*)object->QueryInterface (
                                 scfInterfaceTrait<Interface>::GetID (),
                                 scfInterfaceTrait<Interface>::GetVersion ());
      return csPtr<Interface> (x);
    }

::

    template<class Interface>
    csPtr<Interface> csQueryRegistry (iObjectRegistry *Reg)
    {
      return scfQueryInterfaceSafe<Interface> (
        Reg->Get (scfInterfaceTrait<Interface>::GetName (),
                  scfInterfaceTrait<Interface>::GetID (),
                  scfInterfaceTrait<Interface>::GetVersion ()));
     }
     
::

    csPtr<iBase> csQueryRegistryTag (iObjectRegistry *Reg,
                                     const char* Tag)
    {
      return csPtr<iBase> (Reg->Get (Tag));
    }
    
::

    template<class Interface>
    csQueryRegistryTag (iObjectRegistry *Reg, const char* Tag)
    {
      return scfQueryInterfaceSafe<Interface> (
        Reg->Get (Tag,
                  scfInterfaceTrait<Interface>::GetID (),
                  scfInterfaceTrait<Interface>::GetVersion ()));
    }

Classes
-------

scfInterfaceTrait<class Interface>
''''''''''''''''''''''''''''''''''
Provides ways to get information about an interface:

::

    template<class Interface>
    class scfInterfaceTrait
    {
      public:
      static int GetVersion ()
      {
        return Interface::InterfaceTrait::GetVersion();
      }

      static scfInterfaceID GetID()
      {
        scfInterfaceID& ID = GetMyID();
        if (ID == (scfInterfaceID)(-1))
        {
          ID = iSCF::SCF->GetInterfaceID(GetName ());
          csStaticVarCleanup (CleanupID);
        }
        return ID;
      }

      static char const* GetName ()
      {
        return Interface::InterfaceTrait::GetName ();
      }
    private:
      static scfInterfaceID& GetMyID()
      {
        static scfInterfaceID ID = (scfInterfaceID)-1;
        return ID;
      }
      static void CleanupID()
      {
        GetMyID() = (scfInterfaceID)-1;
      }
    }

The non-specialized template is used for new-style interfaces.
For old-style interfaces using SCF_VERSION macro a specialization is created.
This however means that old-style interfaces cannot be defined in namespaces.


scfImplementation<class Class>
''''''''''''''''''''''''''''''
Base class for the scfImplementaitonN template. Provides functions and data
that is common independent of number of implemented interfaces or the type
of the class:

::

    template<class Class>
    class scfImplementation : public virtual iBase
    {
    public:
      scfImplementation (Class *object, iBase *parent=0)
        : scfObject(object), scfRefCount (1), scfParent (parent), 
          scfWeakRefOwners (0)
      {
        if (scfParent) scfParent->IncRef ();
      }
      
      virtual ~scfImplementation ()
      {
        scfRemoveRefOwners ();
      }

      void DecRef()
      {
        scfRefCount--;
        if (scfRefCount == 0)
        {
          if (scfParent) scfParent->DecRef ();
          delete scfObject;
        }
      }
    
      void IncRef ()
      {
        scfRefCount++;
      }

      int GetRefCount ()
      {
        return scfRefCount;
      }

      void AddRefOwner (iBase** ref_owner)
      {
        if (!this->scfWeakRefOwners)
          scfWeakRefOwners = new csArray<iBase**> (0, 4);
        scfWeakRefOwners->InsertSorted (ref_owner);
      }
  
      void RemoveRefOwner (iBase** ref_owner)
      {
        if (!scfWeakRefOwners) return;
        
        size_t index = scfWeakRefOwners->FindSortedKey (
                         csArrayCmp<iBase**, iBase**> (ref_owner));
                         
        if (index != csArrayItemNotFound) scfWeakRefOwners->DeleteIndex (index);
      }

    protected:
      Class *scfObject;
    
      int scfRefCount;		/* Reference counter */
      iBase *scfParent;
      csArray<iBase**>* scfWeakRefOwners;
        
      void scfRemoveRefOwners ()
      {
        if (!scfWeakRefOwners) return;
        
        for (size_t i = 0 ; i < scfWeakRefOwners->Length () ; i++)
        {
          iBase** p = (*scfWeakRefOwners)[i];
          *p = 0;
        }
        delete scfWeakRefOwners;
        scfWeakRefOwners = 0;
      }
      
      void *QueryInterface (scfInterfaceID iInterfaceID, int iVersion)
      {
        if (iInterfaceID == scfInterfaceTrait<iBase>::GetID () &&
            scfCompatibleVersion (iVersion, scfInterfaceTrait<iBase>::GetInterfaceVersion ()))
        {
          scfObject->IncRef();
          return static_cast<iBase*>(scfObject);
        }
        
        if (scfParent) 
          return scfParent->QueryInterface (iInterfaceID, iVersion);
        
        return 0;
      }
    };


scfImplementationN<class Class, class I1, .., class IN>
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
Limitations in the template syntax leads to that we need one scfImplementationN
class per number of interfaces we implement (N is number of interfaces, we propose
that CS provides implementation from N=1 to N=10 for now). The code in these is 
almost the same

::

    template<class Class, class I1, .., class IN>
    class scfImplementationN : public scfImplementation<Class>,
                               public I1,
                               ...
                               public IN
    {
    public:
      scfImplementationN (Class *object, iBase *parent=0)
        : scfImplementation<Class> (object, parent)
      {}
      
      void *QueryInterface (scfInterfaceID iInterfaceID, int iVersion)
      {
        if (iInterfaceID == scfInterfaceTrait<I1>::GetID () &&
            scfCompatibleVersion (iVersion, scfInterfaceTrait<I1>::GetInterfaceVersion ()))
        {
          scfObject->IncRef();
          return static_cast<I1*>(scfObject);
        }
        ...
        if (iInterfaceID == scfInterfaceTrait<IN>::GetID () &&
            scfCompatibleVersion (iVersion, scfInterfaceTrait2<IN>::GetInterfaceVersion ()))
        {
          scfObject->IncRef();
          return static_cast<IN*>(scfObject);
        }
        
        return scfImplementation<Class>::QueryInterface (iInterfaceID, iVersion);
      }
      
    protected:
      typedef scfImplementationN<Class, I1, I2, .. , IN> scfImplementationType;
      
      virtual ~scfImplementationN ()
      {}
  };
  
scfImplementationExtN<class Class, class I1, .., class IN>
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
This class is very similar to scfImplementationN but is used when an implementation
class extends another with new interfaces.

::

    template<class Class, class Super, class I1, .., class IN>
    class scfImplementationExtN : public Super,
                                  public I1,
                                  ...
                                  public IN
    {
    public:
      scfImplementationExt1 (Class *object)
      : Super (), scfObject (object)
      {
      }
      
      void *QueryInterface (scfInterfaceID iInterfaceID, int iVersion)
      {
        if (iInterfaceID == scfInterfaceTrait<I1>::GetID () &&
            scfCompatibleVersion (iVersion, scfInterfaceTrait<I1>::GetVersion ()))
        {
          scfObject->IncRef();
          return static_cast<I1*>(scfObject);
        }
        return Super::QueryInterface (iInterfaceID, iVersion);
      }

    protected:
      Class* scfObject;
      typedef scfImplementationExt1<Class, Super, In1> scfImplementationType;

      virtual ~scfImplementationN ()
      {}
    };



References and Footnotes
========================
.. [1] SCF -- Shared Class Facility

.. [2] ABC - Abstract Base Class. Class with only pure virtual functions.

.. [3] C++ Standard - ISO 14882/1998.

Copyright
=========
This document has been placed in the public domain.
