|   
      
        
        DynObj - A Plugin Scenario
        This is an example illustrating the usefulness of a generic type based
        approach to plugins. In the first, initial attempt, we 'get caught' in 
        some places where a more general solution is suitable. Then we solve the 
        same problem using a typed approach.
        
        Example: An Audio Filter Plugin
        Suppose we want the ability to load and use 3rd party audio filter
        files from binary plugins. 
        
        Initial Approach
        So, we define a first filter interface class:
        - 
        class AudioFilterI { 
 
        - 
          virtual const char* GetFilterName( );  
 
          virtual bool Apply( const float signal_in[], int signal_length, 
            -   float signal_out[], char err_buf[], int err_buf_len ); 
  
           
          // We also want to be able to enumerate any parameters of this filter   
          virtual bool GetParamName( int param_ix, char name[], int name_len ); 
          virtual bool GetParamValue( int param_ix, char val[], int val_len ); 
          virtual bool SetParamValue( int param_ix, const char *val );   
          
        };
          
        
        Although this could work, there are several things to note:
        
          - The first method is safe and useful, it returns a constant string from the plugin.
          
 - The second method has some problems:
            - We don't know exactly how long the signal_out array is. The assumption is maybe 
                that it's just as long as signal_in. It's not very generic, maybe the output 
                signal will be a bit longer than the input one. 
            
 - We want to be able to communicate if an error happens, using err_buf with
                a given length. Maybe our error message doesn't fit inside the buffer?
            
  
           - For the methods concerned with communicating parameters, we have some more issues:
            - Length of param names and values would be better as dynamic.
            
 - The methods concerned with parameters are really  bit separate from the filtering
                method. It would deserve an interface of its own.
            
  
           
         
        Handling many filter plugins, including those implemented by 3rd parties, this interface 
        is dangerous.
           
        Audio Filter with Generic Types
        Suppose we define a generic interface for signals: 
        - 
        
// %% DYNOBJ class(VObj)  
        class SignalI {  
        - 
          virtual const char* GetName( );  
 
          virtual float GetLength( ); 
          virtual float* GetSamples( ); 
          virtual bool Apply( const SignalI& signal_in, GrowableSignalI& signal_out, 
          
          
        };
          
        
        The first comment  %% DYNOBJ class(VObj) 
        tells the DynObj preprocessor (pdoh) that there comes a class or interface declaration 
        next. It will then generate necessary glue code to expose our interface.
  
        We have used the existing DynStr interface (a generic interface to a Unicode capable
        string class) in the variable err_str. 
  
        Then we define an interface for paramaters:
        - 
        
// %% DYNOBJ class(VObj)  
        class ParamI {  
        - 
          virtual bool GetName( int ix, DynStr& name );
 
          virtual bool GetValue( int ix, DynStr& name ); 
          virtual bool SetValue( int ix, const char* val );  
          
        };
          
        
        Now, we can implement a filter plugin along these lines:
        
        
        The comment  %% DYNOBJ class(VObj) bases(SignalI,ParamI) 
        tells the DynObj preprocessor (pdoh) that we're declaring a class with 'unrooted' 
        base classes, and to expose the interfaces SignalI, ParamI to users of
        the plugin. 
  
        
        Users of our plugin will find the 'side interface' ParamI by performing
        a run-time type query [do_cast<ParamI*>(U* pu)] on it.
  
        Conlusions
        By moving from an initial 'first shot' approach, to a more generic one, we 
        achieve:
        
          - We get safe string handling at no expense in the plugin. It just uses 
              the stable DynStr interface to strings implemented in the main 
              program.
          
 - By moving to a general SignalI we eliminate problems around array 
              length and resizing. We use base class implementations and need not worry
              about this in our class.
          
 - Parameter handling is distilled into a separate interface class. Filtering
              and paramater management are different jobs. Also here, DynStr gives
              us a safe and simple implementation.
        
  
        
        In the initail approach, a plugin could not re-use any of the functionality
        from the main application. It was left to its own. With a type based approach,
        the plugin gets more choice and can also query the main application for 
        interfaces and objects.
        
        Chances are that some of the new interfaces are useful in other situations than 
        audio filtering. The ParamI interface is a candidate to turn into a public 
        interface.
  
        
        We should also note that once there exists binary plugins using or implementing 
        some of these interfaces, they rely on the interfaces being stable. I.e. at that 
        point, we cannot add or change the methods of an interface (it's should be tagged 
        as stable if it's public).
  
        
        Finally, the interface classes above are plain, easy to read C++ classes. The 'big' 
        libraries (COM, xpCOM) use extensive C macros syntax to declare classes and 
        methods. Following such code is more difficult, both for developers and a debugger.
        
  
       |