开发者

Converting to different data types based on different template arguments

开发者 https://www.devze.com 2023-04-05 01:05 出处:网络
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

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号