Is there a way to use preprocessing to conditionally return different types in a template. More specifically, is there a way to use preprocessing to block out conditional parts of code for one type verse another?
Example of what I can't get to compile for anything but POD types:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
template <class T>returnTypeMatching(string inputName,string typeName)
{
if(typeName="string")
{
cerr<<"string types matched so returning string '"<< inputName <<"'"<<endl;
return inputName;
}
if(typeName="double")
{
cerr<<"string types matched so returning double "<< stod(inputName) <<endl;
return stod(inputName);
}
cerr<<"no type matched, so returning blank"<<endl;
T blank;
return blank;
}
If I switch string to long, the implied conversions let it work. I'm imagining there might be some preprocessing way to hide irrelevant conditions. I mean, this code would cause an error to return a string when T=double, so I get why there is a compiler error. However, is there a way to make that part of the code get hidden in that case when the template is processed when T=double? Does this question make any sense?
C++ is strongly typed. This means that you need to know the type of variable you have yourself. I understand what your trying to do with the function, but how would you use it? You need to know what is comming out of that function in order to set it to something, and that means that you know what it should be when using a function.
Generally I would use something like this:
1 2 3 4 5 6 7
template <class T>
T ConvertString(string input, T defaultVal)
{
std::stringstream iss(input);
iss >> defaultVal;
return localVal;
}
At first I didn't catch this, but think maybe I see what you are suggesting!
Basically this would call operator >> to do the conversion. I'll give that a try.
To answer your question, the way I intended to use this was to have a generic class that holds parameters. Then, in code that uses that class, I want to be able to specify the type of parameter. Then this function would be called to retrieve the value in the form of the specified type. At that time, the type is also known and passed into the function to specify the type of template. The irony of this situation is that it works for all cases where I need a conversion from string to a number type, but in the case of string, the original input that doesn't need conversion, it can't compile because the code thinks that could be an option when the type is double (even though I'm making sure that cannot happen). I suppose the simple solution is just to write a separate function for each return type. It's just annoying to do that because I wanted to write portable code that has the same syntax in each case. In other words, I wanted to use a template to compact and simplify the visible code and increase portability. On the other hand, maybe there is a way to specialize a template function, but I find conflicting information about that topic, and I can't find any syntax that works for me.
I'm also starting to wonder if I can do a reinterpret cast, and if I can, if I want to go there. I think if I get your suggestion to work, it will win for sure. If a reinterpret cast works though, maybe it could be a solution. I know which types I'm dealing with, I'm calling the types I'm dealing with, but I want conditional portions of my code to have different return types in cases that would never be encountered. I'm thinking maybe if the only case where code encounters a reinterpret cast is in the case that it reinterprets as itself, maybe that will do the trick. I feel like I'm missing something though--and that's probably the part you just suggested!
Okay, wait! I thought this solved my problem, but it doesn't.
Here's where I run into trouble again--suppose I add this at the end of main above:
1 2 3 4 5 6 7 8 9 10 11
vector<what_class>listOfThese;
listOfThese.push_back(intClass);
listOfThese.push_back(doubleClass);
listOfThese.push_back(stringClass);
listOfThese.push_back(shortClass);
for(auto what:listOfThese)
{
cerr<<"type = "<<what.type<<" and value = "<<what->getValueAsType()<<endl;
}
This doesn't compile because it sees these as different types. I'm trying to create one type that can handle all of these possibilities. In other words, I want to be able to use the code shown here… Is that possible?
#include <iostream>
#include <string>
#include <vector>
usingnamespace std;
class typeClass
{
public:
typeClass(string _type="NotDefinedYet")
:
type(_type)
{
}
void setType(string _type)
{
type=_type;
}
string getType()
{
return type;
}
private:
string type;
};
template <typename T>
struct what_class:public typeClass
{
what_class(string _value)
:
typeClass("NotDefinedYet"),
value(_value)
{
}
T getValueAsType()
{
T blank;
return blank;
}
string value;
};
template <> struct what_class< int >:public typeClass
{
what_class(string _value)
:
typeClass("int"),
value(_value)
{
}
int getValueAsType()
{
int intValue;
try
{
intValue=stoi(value);
}
catch(exception&except)
{
cerr<<"Warning, could not convert the string value to an integer"<<endl;
}
return intValue;
}
string value;
};
template <> struct what_class< double >:typeClass
{
what_class(string _value)
:
typeClass("double"),
value(_value)
{
}
double getValueAsType()
{
double doubleValue;
try
{
doubleValue=stod(value);
}
catch(exception&except)
{
cerr<<"Warning, could not convert the string value to a double"<<endl;
}
return doubleValue;
}
string value;
};
template <> struct what_class< string >:typeClass
{
what_class(string _value)
:
typeClass("string"),
value(_value)
{
}
string getValueAsType()
{
string stringValue;
try
{
stringValue=value;
}
catch(exception&except)
{
cerr<<"Warning, could not convert the string value to a string"<<endl;
}
return stringValue;
}
string value;
};
class holder
{
public:
holder(typeClass *_what)
:
what(_what)
{
}
string getType()
{
return what->getType();
}
typeClass*ptr()
{
return what;
}
private:
typeClass*what;
};
int main()
{
what_class<int>intClass("12");
cout<<intClass.getType()<<" "<<intClass.getValueAsType()<<endl;
what_class<double>doubleClass("11.11");
cout<<doubleClass.getType()<<" "<<doubleClass.getValueAsType()<<endl;
what_class<string>stringClass("abcde");
cout<<stringClass.getType()<<" "<<stringClass.getValueAsType()<<endl;
what_class<short>shortClass("12");
cout<<shortClass.getType()<<" "<<shortClass.getValueAsType()<<endl;
vector<holder>listOfThese;
listOfThese.push_back(holder(&intClass));
listOfThese.push_back(holder(&doubleClass));
listOfThese.push_back(holder(&stringClass));
listOfThese.push_back(holder(&shortClass));
cout<<"Full List:"<<endl;
for(auto what:listOfThese)
{
if(what.getType()=="int")
{
cout<<"type = "<<what.getType()<<" and value = "<<static_cast<what_class<int>* >(what.ptr())->getValueAsType()<<endl;
}
if(what.getType()=="double")
{
cout<<"type = "<<what.getType()<<" and value = "<<static_cast<what_class<double>* >(what.ptr())->getValueAsType()<<endl;
}
if(what.getType()=="string")
{
cout<<"type = "<<what.getType()<<" and value = "<<static_cast<what_class<string>* >(what.ptr())->getValueAsType()<<endl;
}
if(what.getType()=="short")
{
cout<<"type = "<<what.getType()<<" and value = "<<static_cast<what_class<short>* >(what.ptr())->getValueAsType()<<endl;
}
if(what.getType()=="NotDefinedYet")
{
cout<<"type = "<<what.getType()<<" and value = blank"<<endl;
}
}
cout<<"Done"<<endl;
}
int 12
double 11.11
string abcde
NotDefinedYet 0
Full List:
type = int and value = 12
type = double and value = 11.11
type = string and value = abcde
type = NotDefinedYet and value = blank
Done
I think though, that I could have simply skipped templates and used void* to store the value as a casted pointer of a typed value. I guess if I'm going to not really be able to use the template, maybe it's just a waste of code… Is there some other way around this?
int 12
double 11.11
string abcde
short 0
Full List:
type = int and value = 12
type = double and value = 11.11
type = string and value = abcde
type = short and value = 0
Done
Just my 1 cent worth - I don't know a lot about templates, but I will post this in case it is actually useful.
Can you optionally use static_assert to restrict the template types to only the types you want to start with, then type_traits to do the appropriate processing? Will need to have template types for inputName and typeName
Was thinking this might be easier than RTTI, plus no casting and no exceptions.
Here is my simple use of these things, with my thought fixed up by Cubbi: