Template Return Types

Hi,

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!
Last edited on
You could use template specialization:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <string>

template <typename T>
struct what_class{
};

#define SPECIALIZE_CLASS(type, string)      \
template <>                                 \
struct what_class<type>{                    \
    static std::string str;                 \
};                                          \
std::string what_class<type>::str = string

SPECIALIZE_CLASS(int, "It's an int.");
SPECIALIZE_CLASS(double, "It's a double.");
SPECIALIZE_CLASS(std::string, "It's an std::string.");

template <typename T>
std::string what(const T &x){
    return what_class<T>::str;
}

int main(){
    std::cout <<what(12)<<std::endl
              <<what(12.5)<<std::endl
              <<what<std::string>("Hello")<<std::endl;
}
Brilliant! This is exactly what I wanted!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <iostream>
#include <string>
using namespace std;
template <typename T>
struct what_class
{
    T getValueAsType();
    string type;
    string value;
};
template <> struct what_class< int >
{
    what_class(string _value)
    :
    type("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 type;
    string value;
};
template <> struct what_class< double >
{
    what_class(string _value)
    :
    type("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 type;
    string value;
};
template <> struct what_class< string >
{
    what_class(string _value)
    :
    type("double"),
    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 type;
    string value;
};
int main()
{
    what_class<int>intClass("12");
    cout<<intClass.type<<" "<<intClass.getValueAsType()<<endl;
    what_class<double>doubleClass("11.11");
    cout<<doubleClass.type<<" "<<doubleClass.getValueAsType()<<endl;
    what_class<string>stringClass("abcde");
    cout<<stringClass.type<<" "<<stringClass.getValueAsType()<<endl;
}


int 12
double 11.11
double abcde
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?
This seems to work though:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include <iostream>
#include <string>
#include <vector>
using namespace 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
Last edited on
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?
Last edited on
It turns out I'm back where I started, but this helped me figure out the syntax for the function specialization:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class typeClass
{
public:
    typeClass(string _type="NotDefinedYet")
    :
    type(_type)
    {

    }
    void setType(string _type)
    {
        type=_type;
    }
    string getType()
    {
        return type;
    }
private:
    string type;
};
struct whatClass:public typeClass
{
    whatClass(string _type,string _value)
    :
    typeClass(_type),
    value(_value)
    {

    }
    template <typename T> T getValueAsType()
    {
        T blank;
        return blank;
    }
private:
    string value;
};
template <> int whatClass::getValueAsType()
{
    int intValue=stoi(value);
    return intValue;
}
template <> double whatClass::getValueAsType()
{
    double doubleValue=stod(value);
    return doubleValue;
}
template <> string whatClass::getValueAsType()
{
    return value;
}
int main(int argc,char** argv)
{

    whatClass intClass("int","12");
    cout<<intClass.getType()<<" "<<intClass.getValueAsType<int>()<<endl;
    whatClass doubleClass("double","11.11");
    cout<<doubleClass.getType()<<" "<<doubleClass.getValueAsType<double>()<<endl;
    whatClass stringClass("string","abcde");
    cout<<stringClass.getType()<<" "<<stringClass.getValueAsType<string>()<<endl;
    whatClass shortClass("short","12");
    cout<<shortClass.getType()<<" "<<shortClass.getValueAsType<short>()<<endl;

    vector<whatClass>listOfThese;
    listOfThese.push_back(intClass);
    listOfThese.push_back(doubleClass);
    listOfThese.push_back(stringClass);
    listOfThese.push_back(shortClass);

    cout<<"Full List:"<<endl;
    for(auto what:listOfThese)
        {
        if(what.getType()=="int")
            {
            cout<<"type = "<<what.getType()<<" and value = "<<what.getValueAsType<int>()<<endl;
            }
        if(what.getType()=="double")
            {
            cout<<"type = "<<what.getType()<<" and value = "<<what.getValueAsType<double>()<<endl;
            }
        if(what.getType()=="string")
            {
            cout<<"type = "<<what.getType()<<" and value = "<<what.getValueAsType<string>()<<endl;
            }
        if(what.getType()=="short")
            {
            cout<<"type = "<<what.getType()<<" and value = "<<what.getValueAsType<short>()<<endl;
            }
        if(what.getType()=="NotDefinedYet")
            {
            cout<<"type = "<<what.getType()<<" and value = blank"<<endl;
            }
        }
    cout<<"Done"<<endl;
    return 0;
}


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

Sound like RTTI is more like what you want:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class RTTI_Object{
public:
    virtual ~Object(){}
    virtual std::string to_string() const = 0;
    virtual std::string type_name() const = 0;
    virtual void make_snafucated() const = 0;
};

class Int : public RTTI_Object{
    int data;
public:
    Int(int);
    std::string to_string() const;
    std::string type_name() const;
    void make_snafucated() const;
    //Other members
};

class Double : public RTTI_Object{
    double data;
public:
    Double(double);
    std::string to_string() const;
    std::string type_name() const;
    void make_snafucated() const;
    //Other members
};

class String : public RTTI_Object{
    std::string data;
public:
    String(const std::string &);
    std::string to_string() const;
    std::string type_name() const;
    void make_snafucated() const;
    //Other members
};

//...

std::vector<RTTI_Object *> objects;
populate_vector(objects);
for (auto *p : objects){
    std::cout <<p->to_string()<<" is of type "<<p->type_name()<<std::endl;
    p->make_snafucated();
    if (dynamic_cast<String *>(p) != nullptr)
        std::cout <<"It's a string.\n";
}
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:

http://www.cplusplus.com/forum/lounge/111518/

http://www.cplusplus.com/reference/type_traits/

http://en.cppreference.com/w/cpp/language/static_assert
Thanks!
I hadn't been familiar with this stuff, but I can think of 100 times when I wish I had been.
Very useful!
Topic archived. No new replies allowed.