C ++。获取数据的类型名称作为类型,而不是字符串

时间:2012-01-17 11:05:28

标签: c++

在开始之前,我会将问题分成两个方面:

第1部分:



在c ++中我们可以使用typeid来获取数据类型,但它会将数据作为const char*,并且我希望它返回数据的类型。

示例:

int data = 20 ; 
float data2 = 3.14 ; 
char *data3 = "hello world" ;

std::cout<< typeid(data).nam() << endl << endl ;
std::cout<< typeid(data2).nam() << endl << endl ;
std::cout<< typeid(data3).nam() << endl << endl ;

现在我有一个从void *获取数据的函数,并将其转换为另一种类型:

template <typename t >
void print (void *data )
{   

    boost::any _t = static_cast<t> (data);
    cout << boost::any_cast<t> (_t) << endl << endl;
}

如果你知道你的数据类型,现在这样可以正常工作: 示例:

void *mydata = alloca(size_object) ;
void some_function_store_int_data_in_voidpointer( &mydata)
print <int> (mydata); // it's ok .

但是当你有许多不同的数据类型时,这是不切实际的,例如:

void somefunction(args &a , void *dest  )
{
 /*code returnd data */
}
enum args
{
_INT_ ,
_FLOAT_ , 
_CHARPOINTER_ ,
};

vector <void *test> myvector ;
myvector.resize (3) ;


void somefunction(_INT_ , myvector.at(0)  ) ;  // store int in void*
void somefunction(CHARPOINTER , myvector.at(0)  ) ;// store char* in void*
void somefunction(_FLOAT_ , myvector.at(0)  ) ;// store float in void*

print <int> (myvector.at(0));
print <char*> (myvector.at(1));
print <float> (myvector.at(2));

1 - 如果我使用这样的东西

print <typeid(myvector.at(2))> (myvector.at(2));

我收到错误,因为我的数据是浮动的,我将其设为const char*

2 - 如果我的数据很少,也许我可以传递每个值的类型。还行吧。但是如果我有100个不同类型的值呢?

我正在寻找类似:typeid的内容,但它'返回的类型不是`const char *。

第2部分



因为我有avector,所以我将使用这样的for_each算法:

for_each ( myvector.begin() , myvector.end() , print</*what i should pass her int , float ,char* ...or what , */>);

在前面的代码中,我只能将一种类型传递给函数,因此将打印相同类型的数据。否则,将打印不同类型的数据,但完全错误(奇怪的格式)。

因此,如果我通过char*,则int数据将完全错误打印。

我该怎么做?

4 个答案:

答案 0 :(得分:1)

如果您要支持的类型列表有限,请使用boost::variant<int, float, const char*, ...>代替boost::any(或void*)。然后,您可以定义一个访问者来调用打印函数的正确实例。

#include <boost/variant.hpp>
#include <boost/foreach.hpp>
#include <vector>
#include <algorithm>
#include <iostream>

template <class T>
void print(T t)
{
    std::cout << t << '\n';
}

struct print_visitor: boost::static_visitor<void>
{
    template <class T>
    void operator()(T t) const { print(t); }
};

int main()
{
    typedef boost::variant<int, double, const char*> Variant;
    std::vector<Variant> vec;
    vec.push_back(13);
    vec.push_back(3.14);
    vec.push_back("Hello world");
    BOOST_FOREACH(const Variant& v, vec) {
        boost::apply_visitor(print_visitor(), v);
    }
} 

使用void*boost::any,我认为您不能比使用长if链测试所有支持的类型做得更好。

答案 1 :(得分:1)

C ++是一种静态类型语言,因此类型只在编译时以有意义的方式存在,而不是在运行时。在运行时,最好的C ++可以为你提供RTTI,它为你提供像dynamic_cast&lt;&gt;这样的东西。和typeid(),但是仅限于在继承层次结构中提供信息,即如果你有

class Base
class DerivedA : public Base
class DerivedB : public Base

你有一个Base*Base&,你可以知道它是BaseDerivedA还是DerivedB。但是在您的情况下,您只有void*,它完全在任何继承层次结构之外,因此没有与之关联的类型信息。因此,所有typeid()都会告诉你,你有一个void*,它不会告诉你隐藏在它背后的任何类型。

此外还有一个结构:

print <typeid(myvector.at(2))> (myvector.at(2));

也无法在C ++中运行,因为模板的类型也需要在编译时知道。但是,.at(2)的类型只能在运行时知道。

因此,要解决您的问题,您必须自己进行类型处理。这意味着您必须将类型与要存储的对象一起存储,如下所示:

struct Value
{
  enum { kInt, kString } type;
  union { 
    int   vInt;
    char* vChar;
  } value;
};

[...]

Value v;
v.type = Value::kInt;
v.value.vInt = 5;

switch(v.type)
{
   case Value::kInt:
     // do int specific stuff
     break;

   case Value::kString:
     // do string specific stuff
     break;
}

访问者提到的boost::variant<>类基本上以一种很好的方式提供了上述内容。

值得一提的另一件事是decltypedecltype是C ++ 11标准中的新功能,它允许您获取对象的实际类型,因此您可以编写如下代码:

int a;
decltype(a) b;

其中b的类型与a相同,即int。这听起来与你想要的完全一样,但事实并非如此,decltype()具有与以前相同的限制。它只能在编译时已知类型时才能工作,它不能对仅在运行时已知的类型执行任何操作。因此它在你的情况下不起作用,只在做一些更复杂的模板编程时才真正有用。

长话短说,请使用boost::variant<>或给自己写一个以类似方式工作的课程。

答案 2 :(得分:0)

第1部分:我不太确定我是否完全理解您的查询,但您是否看过头文件<typeinfo>struct type_info可能是您正在寻找的。

答案 3 :(得分:0)

  

我怎么能以不同的方式做到这一点?

如果您打算使用相同的功能打印不同的数据格式,那么您可以这样做:

#include <iostream>
#include <algorithm>
#include <vector>

template <typename T> class Callback{
public:
    void operator()(const T& value) const{
        std::cout << value << std::endl;
    }
};

template <typename T> Callback<typename T::value_type> makeCallback(const T&){
    return Callback<T::value_type>();
}

int main(int argc, char** argv){
    std::vector<int> ints(20);
    std::vector<float> floats(20);

    std::fill(ints.begin(), ints.end(), 0);
    std::fill(floats.begin(), floats.end(), 0.0f);

    std::for_each(ints.begin(), ints.end(), makeCallback(ints));
    std::for_each(ints.begin(), ints.end(), makeCallback(floats));

    return 0;
}

但是,如果您希望在同一个std :: vector中存储多种不同的数据类型,那么您需要“变体”类型(例如boost::variantQVariant或类似),并没有办法绕过它。


  

我正在寻找类似的东西:typeid但它'返回的类型不是`const char *。

在C ++中,“type”仅在编译阶段存在,因此您无法返回它,因为在编译程序后它不再存在。没有“类型”,所以你不能退货。

因此,要从对象获取“类型”,您需要实现某种“变体”类型,该类型可以保存任何对象及其类型信息,并传递该“变体”类型。这种系统的一个例子是Qt 4中的QVariant

变体类型的AFAIK实现是这样的:每种类型变体支持都有某种表,您注册该表中必须支持的所有类型变体类。表提供了用于创建类型,销毁类型,(反)序列化类型的函数,以及可能有关该类型的一个对象所需的内存量的信息。该表可以包含您需要的可选信息,您可以将整个注册过程转换为宏+模板组合。正如您所看到的,这不是由编译器自动完成的,而是涉及大量麻烦并且必须由程序员处理的事情。此外,如果程序必须能够采用外部开发的类型(插件等),事情会变得更加有趣。

由于语言限制,更好的想法是避免在可能的情况下需要“返回类型”的情况 - 变体系统并不是很困难,但它们也不是很有趣,因为所有必要的理智检查和转换。示例问题:如果将变量类型的字符串传递给应该使用float的函数,该函数是否应该尝试将字符串转换为float?如果转换失败,它应该崩溃/抛出异常,还是假设该变量具有默认值?如果转换失败的默认值是什么,它应该是什么以及如何传递?等等。这不是一门火箭科学,但处理起来非常烦人。

例如,你可以摆脱“void *”(如果函数将指针作为参数,那么我会假设poitner可以是NULL / 0.所以“void *”参数不是一个好主意) 。函数中的参数和使用模板使编译器为您在程序中实际使用的类型生成所需的代码。如果模板不是一个选项,那么你需要某种“变体”类型(最好由其他人开发),...或者你可以切换到另一种语言,提供你需要的类型信息。你不 使用C ++,任何完成这项工作的工具都可以。依赖于RTTI也不是一个完美的解决方案,因为如果你设法将指针传递给不包含类型信息的东西,你将得到一个非标准的异常(__non_rtti_object)。