|   
      
        
        Limitations with DynObj Runtime Objects
        DynObj enables creating plugin types and using plugin objects almost 
        as ordinary classes and objects are created and used. Even so, there 
        are a number of limitations and issues to keep in mind:
        
        
        
        
        No static linking
        The most obvious one is that we cannot use global functions from a plugin that 
        imply static linking. This includes:
        
          - Ordinary global functions:
          
            float SumVector( float *elems, int size );    // Not in a plugin! 
           
          Non in-line constructors fall in this category.
          
          
          
           - Plain member functions:
            
- 
            
// %% DYNOBJ class(VObj)  
            class SignalI {  
            - 
              virtual const char* GetName( ) = 0;  
 
              virtual int GetLength( ) = 0;   
              virtual float* GetSamples( ) = 0;   
              
              float GetEnergy();       // Not in a plugin!
              
            };
              
            The function GetEnergy() cannot be used by a host, it is 
            not virtual and a linker would report 'Unresolved symbol'.
            
  
            
           - Static member functions:
            These are essentially plain functions that are bound into the 
            namespace of a class. 
        
  
        
        ...inline member functions are OK
        Using inline functions in the interface is OK and quite useful:
          - 
          
// %% DYNOBJ class(VObj)  
          class SignalI {  
          - 
            // Virtual member funcs, see above
 
            inline bool IsEmpty(){ return GetSamples()==NULL || GetLength()==0; }
            
          };
            
        Since both the plugin compiler and the source compiler generate
        code for inline functions as they are used, there is no problem.
        
  
        Inline 'utility' functions in a plugin class can be quite useful,
        they indicate how to use the class and help keeping the virtual 
        function tables small. Their signatures may also be modified after 
        an interface has stabilized, doing that won't break any compiled apps 
        using the plugin.
          
        ...template member functions are OK
        If an inline function is a template function, that is also OK,
        provided that the body of the template function in the plugin 
        header file.
        
        
        No C++ exceptions
        The implementation of C++ exception handling has compiler specific
        parts and linking ambiguities. In many cases, it is not possible to 
        throw and catch C++ exceptions across plugin boundaries.
        
        
        No virtual function name overloading
        For historical reasons, name overloading for a virtual function is 
        to be avoided: 
        
          - 
          
// %% DYNOBJ class(VObj)  
          class SignalI {  
          - 
            // Virtual member funcs, see above
 
            virtual SignalI& Append( SignalI& sig_other );  
            virtual SignalI& Append( float* samples, int length );   // No!  
            
          };
            
        
        What happens is that two different compilers may choose to put the 
        functions with same name in opposite order in the VTable. We risk 
        calling the wrong function in that case.
  
        
        A better solution in this case would be:
        
          - 
          
// %% DYNOBJ class(VObj)  
          class SignalI {  
          - 
            // Virtual member funcs, see above
 
            virtual SignalI& Append( float* samples, int length ); 
  
            // Make the other function inline: 
            inline SignalI& Append( SignalI& sig_other ){  
            - return Append( sig_other.GetSamples(), sig_other.GetLength() );
            
  } 
            
          };
            
        Overloading between inline functions and virtual functions is OK. 
        We get to keep the semantics intended in the first interface and
        keep the VTable size down.
        
        
        No raw allocations for parameters/return values
        We must avoid any situation where memory is allcoated (using any 
        functions in the new/malloc family) on one side of a plugin 
        boundary and an attempt to free it is made on the other side.
  
        
        Different compilers are often linked with different memory allocators
        so we would end up with run-time errors.
        
        
        No virtual destructors
        Different compilers can implement and use virtual destructors in
        slightly different ways. For example, the GCC  compiler usually
        allocates two slots in a VTable for destructors, while Visual
        C++ just uses one. 
  
        
        The ordinary way of destroying objects in the DynObj library is
        to use DynObj::doDestroy(). 
        If you have to expose a plugin class that has a virtual destrcutor,
        you can insert DO_VTABLE_DTOR_PADDING (from doSetup.h) 
        after each such declaration:
        
          - 
          
// %% DYNOBJ class(VObj)  
          class SomeIface {  
          - 
            // Virtual member funcs, see above
 
            virtual ~SomeIface( );  
            DO_VTABLE_DTOR_PADDING;  
            /* Other declarations here */  
            
          };
            
          
        The macro above only makes sure that the VTables end up the same size 
        from perspectives of a host and a plugin. Invoking the virtual 
        destructor over plugin boundaries is not supported by the DynObj 
        library. 
        
        
        Be careful with interface data members
        It is possible to include data members in an interface declaration: 
        
          - 
          
// %% DYNOBJ class(VObj)  
          class ColumnI {  
          - 
            // Virtual member funcs, see above
 
            virtual const char* GetColumnName() = 0;  
             
            int m_column_id;    // Data member! 
            inline int GetId(){ return m_column_id; }  
            
          };
            
          
        If it is clear that each instance of ColumnI needs an ID, 
        that an int is the suitable type for it, and that an application 
        should be allowed to modify it, then, this is a way of achieveing 
        that. 
  
        
        Strictly speaking, ColumnI is no longer an interface, but a 
        class.
  
        Issues to be aware of:
        
          - If we declare several member variables: 
            
- 
              int m_column_id; 
 
              bool m_inserted;  
              short m_width; 
             
  
            the compiler will have many options in laying out the binary 
            object.
  
            We can try to align each member variable on a word boundary, 
            but we have no guarantee that a potential host compiler will 
            use the same data layout even so.
  
          
          - Different language compilers may have problems with handling 
            objects like this. The model in D is for example that 
            interfaces have no data members. 
        
  
          
          
          
        
        
        
        
        
        
        
  
       |