Reputation: 811
I am struggling with allowing user to select data type template will be created as. Since template type must be defined on compile, I must specify data type template will use eg(string,int, so on), but that means I cannot change it latter on, from lets say string to int even if my template supports it, because template class object was declared as string. My class declaration below:
template <class T>
class MyHashTable
{
public:
string deleted="deleted";
unsigned short tableSize;
// array of vectors, hash table container
vector<T>* myTable;
vector<T>* deletionTable;
MyHashTable(unsigned short tableSize) : myTable(new vector<T>[tableSize]), deletionTable(new vector<T>[tableSize])
{
this->tableSize=tableSize;
}
object declaration outside class
MyHashTable <string>* myChainedTable=NULL ;
string tableType;
object initialization
if (myChainedTable)
{
delete myChainedTable;
myChainedTable=NULL;
}
getType();
if (!myChainedTable)
{
if (tableType=="string")
myChainedTable= new MyHashTable<string>(length);
if (tableType=="char")
MyHashTable<char> myChainedTable(length); // no difference with or without using new keyword
if (tableType=="double")
MyHashTable<double> myChainedTable(length);
if (tableType=="float")
MyHashTable<float> myChainedTable(length);
if (tableType=="int")
MyHashTable<int> myChainedTable(length);
cout<<tableType<<" table of size "<< length<<" created"<<endl;
I attempted passing class object to functions instead of having it as global variable, but couldnt get it work either.
What I really need is single template object that can have: int,string,char,double,float types, I have 3 functions that need to have access to template class object, and having 5 different objects and 200 lines of if statements for each situation sounds like worst possible solution. I been stuck on this for a while and just cant figure out how to do it and any help will be appreciated.
void getType()
{
cout<<"Enter table type, types available: int, char, float, double, string.\n";
tableType=getInput();
while((tableType != "int")&&(tableType !="float")&&(tableType !="double")&&(tableType!="char")&&(tableType !="string"))
{
cout<<"Invalid type, please try again "<<endl;;
tableType=getInput();
}
}
Upvotes: 1
Views: 4167
Reputation: 73376
Your question is at the boarder between templates and variants.
The template is compile time. So you have to choose at compile time the type you want for your object. Your conditional approach can't work (see comments to question).
On the other side, you seem to need a dynamic choice of type at runtime.
If you want to go on on template way: (edit based on comments)
You'd need to have all the templates inherit from a single polymorphic base class (one common interface with virtual functions). Example:
class MyHashBase // common base class for all templates
{
public:
virtual void addElement(void *ptrelem) = 0; // adding an element must be implemented by template. With void* since future template type unknown from base class
virtual void displayAll() = 0;
};
The templates would need then implement the virtual functions:
template <class T>
class MyHashTable : public MyHashBase
{
public:
unsigned short tableSize;
vector<T>* myTable; // I leave it as it is, but you could implement these as vector<T> instead of vector<T>*
vector<T>* deletionTable;
MyHashTable(unsigned short tableSize) : myTable(new vector<T>[tableSize]), deletionTable(new vector<T>[tableSize]), tableSize(tableSize)
{ }
void addElement(void* ptrelem)
{ myTable->push_back(*reinterpret_cast<T*>(ptrelem)); } // reinterpret the void* of the common interface as a T*
void displayAll()
{ copy(myTable->begin(), myTable->end(), ostream_iterator<T>(cout, "\n")); }
};
You could then have your myChainedTable
be a pointer to the common base type, and intialise this pointer in the way you did with the string case (i.e. using new
).
MyHashBase *myChainedTable = nullptr;
//...
if (tableType == "string")
myChainedTable = new MyHashTable<string>(length);
else if (tableType == "double")
myChainedTable = new MyHashTable<double>(length);
//...
You could then use the common API, for example if tableType
is "double"
:
double d1 = 3.1415, d2 = 1.4142;
myChainedTable->addElement(&d1); // ATTENTION: you must ensure to provide pointer to the correct data type
myChainedTable->addElement(&d2);
myChainedTable->displayAll();
You'll certainly have a coupe of if required in the calling code, but you could reduce them to minimum by carefully designing the base class (for example, you could add a virtual clone function, to duplicate the data without need to know the type by the caller).
However, using a single signature for the common functions of the base class is cumbersome. To make the virtualisation possible you need to pass parameters through void*
pointer which is not so nice and rather error prone.
Alternate way with variants
You could also use boost variants which are meant for managing objects with dynamic definition of types.
In this case you would not need template for your own data structure. You would create a MyHashTable
with elements of type boost::variant< int, std::string, ... >
.
You could then access to the right value of the object if you know its type (as in your myChainedTable
) by using: boost::get<int> (element)
(or boost::get<string>()
, ...).
If you don't know the type on an element you could use the concept of "visitor" to chose automatically the appropriate function to exectue depending on the type.
Edit: alternate way with unions:
If you're not allowed to use variants another alternative could be use a union
. I don't know the topic of you rassignment, but you have the choice whether you use a union to define the elements (like the variants, without templates) or to use a template type as you did, but define myChainedTable
to be a union of pointers to the different template instantiations. But yes, it requires a lot of if
s...
Upvotes: 2
Reputation: 76240
Templates are resolved at compile time. Your container type is resolved at runtime. Templates are clearly not the solution here. The first thing that comes to my mind is a combination of boost::any
and std::vector
instead.
Upvotes: 0