I have two systems that I need to map types between - the source data fields, which can be numerical, chars, or strings, is all stored as string objects; the destination system needs a different data type for the underlying type of each data field, and I need to dynamically do this mapping.
So basically, for each data field, I have the actual field string 's' and the type of the underlying data, 'type', and I am trying to convert to a type 'dest' based on 'type'. I've been trying to use templates and template constants to hack something that can do this, with no luck.
The current try I ha开发者_如何学Pythonve is the following, but this does not compile due to the conflicting return types:
template<class CLASSTYPE, int CLASSID>
CLASSTYPE returnDifferentTypes()
{
using namespace std;
if (CLASSID == 1) // 1 = "string"
return std::string("returned string");
if (CLASSID == 2) // 2 = int
return 123;
if (CLASSID == 3) // 3 = double
return 123.123;
}
So I've been calling something like
string mapped = returnDifferentTypes<string, 1>()
or
int mapped = returnDifferentTypes<int, 2>()
Can anyone recommend a smarter cleaner way to do this? Ideally I'm trying to return the appropriate return type with just a string representing the type to map it to. Thanks in advance.
For your case, CLASSID
is a redundant parameter; so omit it.
You can simply specialize the method returnDifferentTypes
for different data types for cleaner way.
template<class CLASSTYPE>
CLASSTYPE returnDifferentTypes(); // undefined
template<>
std::string returnDifferentTypes<std::string>() { // for 'std::string'
return std::string("returned string");
}
template<>
int returnDifferentTypes<int>() { // for 'int'
return 123;
}
template<>
double returnDifferentTypes<double>() { // for 'double'
return 123.123;
}
Usage:
string mapped = returnDifferentTypes<string>();
int mapped = returnDifferentTypes<int>();
if you can discern the ID statically, template pecialization is probably better that a network of if-s.
template<int id>
struct multireturn; // not defined
template<>
struct multireturn<1>
{
typedef std::string return_type;
return_type operator()() { return "string"; }
};
template<>
struct multireturn<2>
{
typedef int return_type;
return_type operator()() { return 12; }
};
template<>
struct multireturn<3>
{
typedef double return_type;
return_type operator()() { return 12.3; }
};
///call like this
astring = multireturn<1>()()
aint = multireturn<2>()()
adouble = multireturn<3>()()
Similarly, you can use function overloading:
enum ret_string_t { ret_string; }
enum ret_int_t { ret_int; }
enum ret_ouble_t { ret_double; }
std::string multireturn(ret_string_t) { return "string"; }
int multireturn(ret_int_t) { return 12; }
int multireturn(ret_double_t) { return 12.34; }
/// call as:
astring = multireturn(ret_string);
aint= multireturn(ret_intg);
adouble= multireturn(ret_double);
Or - less orthodox, but still working-
struct multireturn
{
operator std::string() { return "text"; }
operator int() { return 10; }
operator doble() { return 3.5; }
};
///call as:
astring = multireturn();
aint = multireturn();
adouble = multireturn();
Of course, const correctness must adjusted as required in all examples.
Unless you use some kind of Variant class instead of a string
to store everything, you will at some point in your code need to have a form of branch that picks between types. However, the conversion function itself is not the place for it. The conversion function can only return one type. If you try to branch within it, it will need to return three types. Again unless you use a Variant or some other ugly hack (eg void*
), you'll never get this to work.
Function template specialization is not needed here assuming you accept having a branch someplace that discriminates between types. A simple CvtTo
function template can do the job:
#include <sstream>
#include <string>
template<class T> T CvtTo(const std::string& s)
{
stringstream ss;
ss << s;
T ret;
ss >> ret;
return ret;
}
Now fully embrace your need for branching logic and call CvtTo
when you're about to store the data:
// SOME DATABASE FUNCTION N STUFF
std::string value = ...; // VALUE TO BE CONVERTED & STORED
enum FieldType {String, Int, Float};
FieldType field_type = ...;
switch( field_type )
{
case String :
store_a_string(CvtTo<std::string>(value)); // note this is not strictly necesarry for String
break;
case Int :
store_an_int(CvtTo<int>(value));
break;
case Float :
store_a_float(CvtTo<float>(value));
break;
}
// FURTHER MAGIC...
If you really want a function that can return multiple types, you're looking for boost::variant
or possibly boost::any
.
However, please at least take a moment to consider why you want this behavior and if there's an alternate approach that might remove the need to return multiple different types from the same function.
If you want to convert numerical values or other "iostream serializable" types to and from string, you could use boost::lexical_cast
.
精彩评论