I'm looking to try to write some generic code that can handle different data types. Once these data types are set, then it'll remain the same throughout the duration of the instance.
I think it'll be easier to show what I'm trying to do, rather than describe it.
helper.h
#include <iostream>
#include <type_traits>
#include <utility>
#include <string>
#include <sstream>
#include <stdexcept>
using namespace std;
template <typename T>
class helper
{
public:
helper()
{
stringstream temp;
if(is_same<T, short>::value)
{
temp << 1;
}
else if(is_same<T,long>::value)
{
temp << 1024;
}
else if(is_same<T, char*>::value)
{
temp << "Hello";
}
else if(is_same<T, string>::value)
{
temp << "Hello World";
}
else
{
throw invalid_argument("Error in helper: Unknown data type" + to_string(__LINE__) + string(__FILE__));
}
temp >> data;
}
T getData()
{
return data;
}
protected:
T data;
};
call.cpp
#include <iostream>
#include "helper.h"
using namespace std;
int main()
{
helper<> my_helper;
int data;
cin >> data;
switch(data)
{
case 1:
my_helper = helper<short>;
break;
case 2:
my_helper = helper<long>;
break;
case 3:
my_helper = helper<char *>;
break;
default:
my_helper = helper<string>;
break;
}
cout << my_helper.getData() << endl;
return 0;
}
Now, this won't compile because helper doesn't have a template argument, but is there a way that I can set the argument at a later point in time (like after there is user input as shown in the example)? Once the argument is set, there is not use case where it'll change. I know this is a trivial example where I could just do a cout
in the switch-case
, but this is the concept that I want to accomplish.
Unfortunately I'm stuck with C++11 and no boost libs, otherwise I think I could use std::any
, I thought I could use void
pointers, but then I'd have to specify what the datatype is when I call a reinterpret_cast
.
If there is any additional information that I can provide or anything I can clear up, please let me know!
The answer to your question depends on the goals you are trying to achieve. So the first. Do not forget that templates and OOP are generally orthogonal. Each template specialization is a new type that is not related to others. Therefore, the only way to cast all specializations to the same type is to inherit from one base class. So
class AbstractHelper
{
public:
virtual ~AbstractHelper() {}
};
template <typename T>
class helper : public AbstractHelper
Second. You need to answer the question - can the data of your template classes be processed by a single handler or is it impractical (based on performance considerations, implementation complexity, etc.) Suppose it can, for example as strings. In this case you need:
class AbstractHelper
{
public:
virtual string getDataString() { return ""; }
...
template <typename T>
class helper : public AbstractHelper
{
public:
string getDataString()
{
return to_string(data);
}
If this is not acceptable, you need to answer the question, is it possible to completely encapsulate the data processing? Suppose you can. Then
class AbstractHelper
{
public:
virtual void printData() { }
...
template <typename T>
class helper : public AbstractHelper
{
public:
void printData()
{
cout << data << endl;;
}
Finally, the most difficult option is that you need different processing for all types and it cannot be encapsulated. Then you need to determine the source type and use dynamic type casting:
enum Type
{
TYPE_SHORT,
TYPE_LONG,
TYPE_STRING
...
class AbstractHelper
{
protected:
Type type_;
public:
Type getType() { return type_; }
......
template <typename T>
class helper : public AbstractHelper
{
public:
helper()
{
stringstream temp;
if (is_same<T, short>::value)
{
temp << 1;
type_ = TYPE_SHORT;
}
else if (is_same<T, long>::value)
{
temp << 1024;
type_ = TYPE_LONG;
}
...
And select the desired handler:
switch (my_helper.getType())
{
case TYPE_SHORT:
cout << dynamic_cast<helper<short>&>(my_helper).getData() << endl;
break;
case TYPE_LONG:
cout << dynamic_cast<helper<long>&>(my_helper).getData() << endl;
Do not be confused by the amount of manual code - macros and templates can significantly reduce it.