Hiding template in non-template class

M

mathieu

Hello,

I did read the FAQ on template(*), since I could not find an answer
to my current issue I am posting here. I have tried to summarize my
issue in the following code (**).
Basically I am trying to hide the `complexity` of template from the
user interface. If you look at the code DataSet should be the object
that my user manipulate. Unfortunately by doing so the object returned
by DataSet::Get is a FloatingPt, so without the virtual keyword, the
DataSet does not correctly hide the templated class.
Basically I am asking on design to resolve this issue, how one
usually hide template class in a non-templated class without making my
FloatingPt class virtual ? If this is detailed in the C++ FAQ please
give me keywords associated with this problem.

Thanks a bunch !
Mathieu


(*)
http://www.parashift.com/c++-faq-lite/templates.html

(**)
#include <iostream>
#include <vector>

class FloatingPt {
public:
/*virtual*/void Type() { std::cout << "FloatingPt" << std::endl; }
};
class Float : public FloatingPt {
public:
void Type() { std::cout << "Float" << std::endl; }
float f;
};
class Double : public FloatingPt {
public:
void Type() { std::cout << "Double" << std::endl; }
double d;
};

template <typename T>
class TDataSet
{
public:
TDataSet(int n = 10):Internal(n) {}
T &Get(int i) { return Internal; }
private:
std::vector<T> Internal;
};

class DataSet
{
public:
DataSet(bool t = false):isfloat(t) {}
FloatingPt &Get(int i) {
if( isfloat )
return FloatDS.Get(i);
else
return DoubleDS.Get(i);
}
private:
bool isfloat;
TDataSet<Float> FloatDS;
TDataSet<Double> DoubleDS;
};

int main(int , char *[])
{
DataSet ds;
ds.Get(0).Type();

return 0;
}
 
I

ivan.leben

What are you actually trying to achieve by hiding the template nature
of the DataSet class? Is this just because you prefer the sintax

DataSet ds(true|false);

over

DataSet<float|double> ds;

? The design you are currently using is bad, because you have to
include two members inside your class each of one type and thus making
your structure larger in order to be able to store values according to
a parameter in constructor of the class. Templates exist for the very
sake of allowing the same functionality to be applied to different
types without rewriting the code or storing variables of multiple
types. Why exactly do you prefer defining the type used through the
constructor function argument instead of through the template argument
as it is ment to be?
 
I

ivan.leben

If your only goal is that the Type() function should print out the type
member variable you could solve this writing a template class, but
write it's specializations for types float and double:

template<typename T>
class DataSet {
T myfloat;
void Type() {std::cout << "Some other type" << std:endl;}
};

template<>
class DataSet<float> {
float myfloat;
void Type() {std::cout << "float type" << std::endl;}
};

template<>
class DataSet<double> {
double myfloat;
void Type() {std::cout << "double type" << std::endl;}
};

What are you actually trying to achieve by hiding the template nature
of the DataSet class? Is this just because you prefer the sintax

DataSet ds(true|false);

over

DataSet<float|double> ds;

? The design you are currently using is bad, because you have to
include two members inside your class each of one type and thus making
your structure larger in order to be able to store values according to
a parameter in constructor of the class. Templates exist for the very
sake of allowing the same functionality to be applied to different
types without rewriting the code or storing variables of multiple
types. Why exactly do you prefer defining the type used through the
constructor function argument instead of through the template argument
as it is ment to be?

Hello,

I did read the FAQ on template(*), since I could not find an answer
to my current issue I am posting here. I have tried to summarize my
issue in the following code (**).
Basically I am trying to hide the `complexity` of template from the
user interface. If you look at the code DataSet should be the object
that my user manipulate. Unfortunately by doing so the object returned
by DataSet::Get is a FloatingPt, so without the virtual keyword, the
DataSet does not correctly hide the templated class.
Basically I am asking on design to resolve this issue, how one
usually hide template class in a non-templated class without making my
FloatingPt class virtual ? If this is detailed in the C++ FAQ please
give me keywords associated with this problem.

Thanks a bunch !
Mathieu


(*)
http://www.parashift.com/c++-faq-lite/templates.html

(**)
#include <iostream>
#include <vector>

class FloatingPt {
public:
/*virtual*/void Type() { std::cout << "FloatingPt" << std::endl; }
};
class Float : public FloatingPt {
public:
void Type() { std::cout << "Float" << std::endl; }
float f;
};
class Double : public FloatingPt {
public:
void Type() { std::cout << "Double" << std::endl; }
double d;
};

template <typename T>
class TDataSet
{
public:
TDataSet(int n = 10):Internal(n) {}
T &Get(int i) { return Internal; }
private:
std::vector<T> Internal;
};

class DataSet
{
public:
DataSet(bool t = false):isfloat(t) {}
FloatingPt &Get(int i) {
if( isfloat )
return FloatDS.Get(i);
else
return DoubleDS.Get(i);
}
private:
bool isfloat;
TDataSet<Float> FloatDS;
TDataSet<Double> DoubleDS;
};

int main(int , char *[])
{
DataSet ds;
ds.Get(0).Type();

return 0;
}
 
M

mathieu

[sorry for top posting]

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...

Hopefully this is clearer now
Mathieu

What are you actually trying to achieve by hiding the template nature
of the DataSet class? Is this just because you prefer the sintax

DataSet ds(true|false);

over

DataSet<float|double> ds;

? The design you are currently using is bad, because you have to
include two members inside your class each of one type and thus making
your structure larger in order to be able to store values according to
a parameter in constructor of the class. Templates exist for the very
sake of allowing the same functionality to be applied to different
types without rewriting the code or storing variables of multiple
types. Why exactly do you prefer defining the type used through the
constructor function argument instead of through the template argument
as it is ment to be?

Hello,

I did read the FAQ on template(*), since I could not find an answer
to my current issue I am posting here. I have tried to summarize my
issue in the following code (**).
Basically I am trying to hide the `complexity` of template from the
user interface. If you look at the code DataSet should be the object
that my user manipulate. Unfortunately by doing so the object returned
by DataSet::Get is a FloatingPt, so without the virtual keyword, the
DataSet does not correctly hide the templated class.
Basically I am asking on design to resolve this issue, how one
usually hide template class in a non-templated class without making my
FloatingPt class virtual ? If this is detailed in the C++ FAQ please
give me keywords associated with this problem.

Thanks a bunch !
Mathieu


(*)
http://www.parashift.com/c++-faq-lite/templates.html

(**)
#include <iostream>
#include <vector>

class FloatingPt {
public:
/*virtual*/void Type() { std::cout << "FloatingPt" << std::endl; }
};
class Float : public FloatingPt {
public:
void Type() { std::cout << "Float" << std::endl; }
float f;
};
class Double : public FloatingPt {
public:
void Type() { std::cout << "Double" << std::endl; }
double d;
};

template <typename T>
class TDataSet
{
public:
TDataSet(int n = 10):Internal(n) {}
T &Get(int i) { return Internal; }
private:
std::vector<T> Internal;
};

class DataSet
{
public:
DataSet(bool t = false):isfloat(t) {}
FloatingPt &Get(int i) {
if( isfloat )
return FloatDS.Get(i);
else
return DoubleDS.Get(i);
}
private:
bool isfloat;
TDataSet<Float> FloatDS;
TDataSet<Double> DoubleDS;
};

int main(int , char *[])
{
DataSet ds;
ds.Get(0).Type();

return 0;
}
 
M

mathieu

[Sorry for top posting]

Ivan,

Thanks again for trying to address my issue. But I am having a hard
time understanding your proposed solution. DataSet is still a template
class in your exemple, right ? How would you design a 'Reader' class
that take as input an istream (containing either float or double value)
and returning a DataSet ?
I would like to keep the template code for the development but propose
a non templated interface for the user. I am sure there are some
pattern for doing it properly.

Thanks
Mathieu

If your only goal is that the Type() function should print out the type
member variable you could solve this writing a template class, but
write it's specializations for types float and double:

template<typename T>
class DataSet {
T myfloat;
void Type() {std::cout << "Some other type" << std:endl;}
};

template<>
class DataSet<float> {
float myfloat;
void Type() {std::cout << "float type" << std::endl;}
};

template<>
class DataSet<double> {
double myfloat;
void Type() {std::cout << "double type" << std::endl;}
};

What are you actually trying to achieve by hiding the template nature
of the DataSet class? Is this just because you prefer the sintax

DataSet ds(true|false);

over

DataSet<float|double> ds;

? The design you are currently using is bad, because you have to
include two members inside your class each of one type and thus making
your structure larger in order to be able to store values according to
a parameter in constructor of the class. Templates exist for the very
sake of allowing the same functionality to be applied to different
types without rewriting the code or storing variables of multiple
types. Why exactly do you prefer defining the type used through the
constructor function argument instead of through the template argument
as it is ment to be?

Hello,

I did read the FAQ on template(*), since I could not find an answer
to my current issue I am posting here. I have tried to summarize my
issue in the following code (**).
Basically I am trying to hide the `complexity` of template from the
user interface. If you look at the code DataSet should be the object
that my user manipulate. Unfortunately by doing so the object returned
by DataSet::Get is a FloatingPt, so without the virtual keyword, the
DataSet does not correctly hide the templated class.
Basically I am asking on design to resolve this issue, how one
usually hide template class in a non-templated class without making my
FloatingPt class virtual ? If this is detailed in the C++ FAQ please
give me keywords associated with this problem.

Thanks a bunch !
Mathieu


(*)
http://www.parashift.com/c++-faq-lite/templates.html

(**)
#include <iostream>
#include <vector>

class FloatingPt {
public:
/*virtual*/void Type() { std::cout << "FloatingPt" << std::endl; }
};
class Float : public FloatingPt {
public:
void Type() { std::cout << "Float" << std::endl; }
float f;
};
class Double : public FloatingPt {
public:
void Type() { std::cout << "Double" << std::endl; }
double d;
};

template <typename T>
class TDataSet
{
public:
TDataSet(int n = 10):Internal(n) {}
T &Get(int i) { return Internal; }
private:
std::vector<T> Internal;
};

class DataSet
{
public:
DataSet(bool t = false):isfloat(t) {}
FloatingPt &Get(int i) {
if( isfloat )
return FloatDS.Get(i);
else
return DoubleDS.Get(i);
}
private:
bool isfloat;
TDataSet<Float> FloatDS;
TDataSet<Double> DoubleDS;
};

int main(int , char *[])
{
DataSet ds;
ds.Get(0).Type();

return 0;
}
 
I

ivan.leben

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
 
I

ivan.leben

.... and another version using type-casted pointer to a vector class

class Reader
{
bool isDoublePrecision;
void *data;

Reader(): isDoublePrecision(false), data(NULL) {}

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 vector<float>;
//read floats into data
}
if (is_double_precision) {
isDoublePrecision = true;
data = new vector<double>;
//read doubles into data
}
}

double getData(int index) {
if (isDoublePrecision)
return ((vector<double>*)data)->at(index);
else
return ((vector<float>*)data)->at(index);
}

};

Note that these reader classes are in fact just a simplification of all
your code in the first post. It just shows you don't have to use
inheritance and templates to implement all this. Just use a type-casted
pointer to something and that's all the trick there is. However, I must
warn again that this is not a nice C++ practice and it's very unsafe
and error-prone, but I think it's the only way you can get rid of
template classes and still allocate only 1 data storage instead of 1
for each data type.
 
M

mathieu

store them appropriately for the user. Did I get it right?

yes :)
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:

template were useful to avoid duplicating code, right ?
//abstract interface
class FP { ....
};

//specialized classes
class FP_Float { ....
};

class FP_Double { ....
};

//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!

Actually adding dynamic binding (virtual) to FP adds 4 more bytes to
the object size.
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.

So in general with two objects, you rely on the fact you can cast one
to the other. And use the 'more precise' one to define your interface.
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
{ ....
};

But always remember to properly cast the pointer any time you might be
using it.

I just came accross the Iterator and Visitor pattern. It looks to me
that I can avoid the problem by never exposing the internal type (in my
case: float or double). Let me study those.

Thanks anyway !
Mathieu
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top