I have tried to describing my issue in as little code as possible.
Apparently with too simple code. Instead of DataSet of my previous try
to see a a Factory Method approach.
Ex: User is reading files with set of floating points value: either
float or double. In the end the file reader will construct a DataSet,
the user should not have to know the floating type contains in the file
to instanciate the reader.
Another problem I am facing is to contruct a python interface to my
library (using swig). In which case I cannot have any template
classes...
So if I understand right, you want the user to be able to create an
instance of this reader class without specifying which type of floating
points it is going to read. Instead the internal functions of the class
would find out what kind of numbers there are in the file and read and
store them appropriately for the user. Did I get it right?
Now I assume you want the two structures specialized for floats and
doubles in order to save space in memory and save floats instead of
doubles when the numbers in file are of smaller accuracy. This can be
achieved without two specialized templates. Instead just write two
dedicated classes and an abstract one to define the interface. However,
this design (with no templates) has many flaws. Let me illustrate with
these three classes:
//abstract interface
class FP {
virtual bool isDouble() { return false; }
void set(double d) { }
double get() { }
//you have to use double so you don't loose
//accuracy in FP_Double
};
//specialized classes
class FP_Float {
float f;
bool isDouble() { return false; }
void set(double d) { this->f = d; }
double get() { return this->f; }
};
class FP_Double {
double d;
bool isDouble() { return true; }
void set(double d) { this->d = d; }
double get() { return this->d; }
};
//reader
class Reader {
//stores pointers to FP structures
vector<FP*data;
void read(const char *filename) {
//do whatever you want to read a number...
//...when you get the number:
FP *newFP;
if (is_single_precision)
newFP = new FP_Float;
if (is_double_precision) {
newFP = new FP_Double;
//assign value
newFP->set(number_read);
data.push_back(newFP);
}
};
Now here come the problems:
1. One could think we saved 4 bytes per variable since we stored floats
instead of doubles. But that is not entirely true - that's because we
allocated the FP_Double and FP_Float classes dinamically and stored
pointers to them (which are of size 4 bytes on a 32-bit architecture)
in the vector instead. This means we allocated additional 4 bytes per
each number!
2. We had to use a single interface for both classes (always using
doubles in the get/set functions) in order not to loose accuracy in the
FP_Double class.
These are exactly the problems which templates were designed to solve.
Unfortunately template types are resolved at compile-time, but your
kind of problem requires determining the storage type of numbers at
run-time. This is the source of your problem. In fact there is no way
to safely avoid template classes when you want to have a single storage
of "variable" size. Here variable is of course meant "at compile time".
An unsafe (and error-prone) way is to manually allocate enough memory
to store the data in and type-cast the pointer to it according to your
knowledge about what kind of data there should be. The vector (and all
the rest from the standard template library) were designed as template
classes in order to remove this unsafeness. But if you want to try it I
can show you a simple example. It will not implement a resizible data
structure (like vector) but instead just show the principle.
class Reader
{
bool isDoublePrecision;
void *data;
Reader(): isDoublePrecision(false), data(NULL) {}
~Reader() { if(data) delete data; }
void readFile(const char *filename) {
//1.somehow determine the type and number of floats in the file
//2. allocate the space needed
if (is_single_precision) {
isDoublePrecision = false;
data = new float[number_of_floats];
//read floats into data array
}
if (is_double_precision) {
isDoublePrecision = true;
data = new double[number_of_floats];
//read doubles into data array
}
}
double getData(int index) {
if (isDoublePrecision)
return ((double*)data)[index];
else
return ((float*)data)[index];
}
};
But always remember to properly cast the pointer any time you might be
using it.
greets,
Ivan Leben