double ivar[] vs. float ivar[]

David Gobbi dgobbi at irus.rri.on.ca
Sat Apr 29 13:31:12 EDT 2000


Hi All,

Just a quick rehash of the email that I've copied below:  There is
a way to use proxy classes to allow you to (functionally, at least)
define both of the following methods:

 float  *GetIvar();
 double *GetIvar();

This would allow us VTK developers to change existing float[] ivars
to double[] or vice versa without introducing compatibility problems
with existing VTK applications.

To demonstrate how this is done, I've attached a short test program.
To compile it, use "CC vtkdouble3.cpp -o vtkdouble3" i.e. linking
to VTK is not required.

The proxy classes in the test program, unlike the one in the email
below, should have zero CPU overhead unless a type conversion is
necessary.

 - David



On Fri, 28 Apr 2000, David Gobbi wrote:

> Hi All,
> 
> There is a marked lack of consistency in VTK whether floating-point
> ivars are double or float.  Or rather, there is some consistency...
> new classes generally use double, older classes use float.
> 
> My guess is that most people would prefer that all of the 
> float[] ivars were converted to double[], but this hasn't been
> done because changing all of those 'float *GetIvar()' methods to
> 'double *GetIvar()' would break compatibility with many applications
> that use VTK.
> 
> 
> There is a way around this!  By using proxy classes you can (as far
> as the users are concerned) define both of the following:
> 
> float  *GetIvar();
> double *GetIvar();
> 
> 
> Here's how to do it:  first, we create a proxy class such as the
> following:
> 
> class vtkdouble3
> {
> public:
>   vtkdouble3(double *dp) { 
>     this->fdata[0] = this->ddata[0] = dp[0]; 
>     this->fdata[1] = this->ddata[1] = dp[1]; 
>     this->fdata[2] = this->ddata[2] = dp[2]; }; 
>   operator double *() { return this->ddata; };
>   operator float *() { return this->fdata; };
>   double operator[](int i) { return this->ddata[i]; };
>   double operator*() { return this->ddata[0]; };
> 
> private:
>   double ddata[3];
>   float  fdata[3];
>   vtkdouble3();
> };
> 
> 
> Then, we define a VTK class that uses this proxy class:
> 
> class vtkClass : public vtkObject
> {
> public:
>   vtkdouble3 GetIvar() { return vtkdouble3(this->Ivar); }; 
>  
> protected:
>   double Ivar[3];
> }
> 
> 
> Now, in our VTK code we can do this:
> 
> vtkClass *myClass = vtkClass::New();
> 
> float  *fp = myClass->GetIvar();
> double *dp = myClass->GetIvar();
> 
> 
> Here's how it works:  the GetIvar() method creates a vtkdouble3
> object and returns it on the stack.  The vtkdouble3 object consists
> of an array of 3 doubles and another array of 3 floats.  The 
> vtkdouble3 array class also has type-conversion methods that will
> allow it to be converted to either float * (i.e. return a pointer
> to its float array) or double * (i.e. return a pointer to its
> double array).
> 
> 
> There is an added advantage to using a proxy class.  Because the
> use of the proxy class involves passing the contents of the Ivar
> array on the stack, instead of simply passing a pointer to the
> Ivar array, using a proxy class guarantees that the GetIvar() method
> is thread safe.  
> 
> 
> There is a downside, when you use proxy classes the method
> 
> double *dp = myClass->GetIvar();
> 
> is slightly less efficient than
> 
> double dp[3];
> myClass->GetIvar(dp);
> 
> whereas it used to be more efficient.  Of course, everybody should
> have been using myClass->GetIvar(dp) most of the time, anyway, because
> of thread safety and related issues.
> 
> 
> So, finally, here is my proposal:  
> 
> Most of those float Ivar[]'s really should be converted to 
> double Ivar[], but we don't want to break people's VTK
> applications.  So, we convert the Ivar[]'s to double 
> but at the same time we write a special 
> vtkGetVectorDoubleNMacro(Ivar) method that returns the
> proxy class instead of a pointer, and that also supports
> GetIvar(float a[N]) and GetIvar(float *a1, float *a2, float *a3).
> A special vtkSetVectorDoubleNMacro(Ivar) would be required, as well.
> 
> Because the majority of the changes would occur within these
> Set and Get macros they could be achieved without an immense
> amount of effort. There are many classes that have float
> *GetIvar() methods that don't use the vtkGet macros, and these
> would have to be handled individually.  Also, the tcl/python/java
> wrappers would have to be modified to recognize the new macros
> and the new proxy classes.
> 
>  - David
> 
-------------- next part --------------
#include <stdio.h>

// proxy class for converting 'double *' into 'float *'
class vtkdouble3
{
public:
  vtkdouble3(double *dp) {this->data = dp;};
  operator double *() { return this->data; };
  operator float *() {for (int i=0; i<3; i++) {this->fdata[i]=this->data[i];};
                       return this->fdata; };
  double operator[](int i) {return this->data[i];};
  double operator*() {return this->data[0];};

private:
  vtkdouble3();
  double *data;
  float fdata[3];
};

// proxy class for converting 'float *' into 'double *'
class vtkfloat3
{
public:
  vtkfloat3(float *fp) {this->data = fp;};
  operator float *() {return this->data; };
  operator double *() {for (int i=0; i<3; i++) {this->ddata[i]=this->data[i];};
                       return this->ddata; };
  double operator[](int i) { return this->data[i]; };
  double operator*() { return this->data[0]; };

private:
  vtkfloat3();
  float *data;
  double ddata[3];
};

// an example VTK class that uses the above classes
class vtkTestClass
{
public:
  static vtkTestClass *New() { return new vtkTestClass; };
  void Delete() { delete this; };

  void SetDoubleIvar(double x, double y, double z) { 
      this->DoubleIvar[0]=x; this->DoubleIvar[1]=y; this->DoubleIvar[2]=z; };
  
  void SetFloatIvar(float x, float y, float z) { 
      this->FloatIvar[0]=x; this->FloatIvar[1]=y; this->FloatIvar[2]=z; };
  
  vtkdouble3 GetDoubleIvar() { return vtkdouble3(this->DoubleIvar); };

  vtkfloat3 GetFloatIvar() { return vtkfloat3(this->FloatIvar); };

protected:
  vtkTestClass() { 
    this->DoubleIvar[0] = this->DoubleIvar[1] = this->DoubleIvar[2] = 0.0; 
    this->FloatIvar[0] = this->FloatIvar[1] = this->FloatIvar[2] = 0.0f; };
  ~vtkTestClass() {};

  double DoubleIvar[3];
  float FloatIvar[3];
}; 

// a short test program
int main(int argc, char *argv[])
{
  double *dp;
  float *fp;
  vtkTestClass *testClass = vtkTestClass::New();

  // test vtkdouble3
  testClass->SetDoubleIvar(1.11111111111111111111, 
			   2.22222222222222222222, 
			   3.33333333333333333333);

  dp = testClass->GetDoubleIvar();
  fp = testClass->GetDoubleIvar();

  fprintf(stderr,"float:  %.16f %.16f %.16f\n",fp[0],fp[1],fp[2]);
  fprintf(stderr,"double: %.16f %.16f %.16f\n\n",dp[0],dp[1],dp[2]);

  // test vtkfloat3
  testClass->SetFloatIvar(1.11111111111111111111, 
			  2.22222222222222222222, 
			  3.33333333333333333333);

  dp = testClass->GetFloatIvar();
  fp = testClass->GetFloatIvar();

  fprintf(stderr,"float:  %.16f %.16f %.16f\n",fp[0],fp[1],fp[2]);
  fprintf(stderr,"double: %.16f %.16f %.16f\n\n",dp[0],dp[1],dp[2]);


  testClass->Delete();
}


More information about the vtk-developers mailing list