Reputation: 135
Before i start i will divide the problem into two parties:
PART 1 :
In c++ to get type of data we can use typeid
but it's give you the data as const char*
,and i want it to return the type of the data.
Example:
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 ;
Now i have a function that get data from void* , and convert it to another type :
template <typename t >
void print (void *data )
{
boost::any _t = static_cast<t> (data);
cout << boost::any_cast<t> (_t) << endl << endl;
}
Now this works fine if you know your data type: Example:
void *mydata = alloca(size_object) ;
void some_function_store_int_data_in_voidpointer( &mydata)
print <int> (mydata); // it's ok .
But this is impractical when you have lots of different datatypes, like this:
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 - If i use something like this
print <typeid(myvector.at(2))> (myvector.at(2));
i get an error because my data is float and I make it const char*
2 - Perhaps I can pass the type of every value if I have few data. This is OK. But what if I have 100 values from different types!
I am looking for something like: typeid
but it' return the type not `const char*.
PART 2
because I have avector I will use a for_each algorithm like this:
for_each ( myvector.begin() , myvector.end() , print</*what i should pass her int , float ,char* ...or what , */>);
In the previous code I can pass only one type to the function so the data from the same type will print. Else the data that are not the same type will print, but completely wrong (Strange format).
So if I pass char*
the int
data will print completely wrong.
How can I do this differently?
Upvotes: 1
Views: 2881
Reputation: 26419
How can I do this differently?
If your intention is to use same function for printing different data formats, then you can do it like this:
#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;
}
However, if you want to store several different data types in same std::vector, then you need "variant" types (like boost::variant
, QVariant
or similar), and there's no way around it.
I am looking for something like: typeid but it' return the type not `const char*.
In C++ "type" exists only at compilation stage, so you cannot return it, because it no longer exists once program has been compiled. There's no "type", so you can't return it.
So to get a "type" from object you need to implement some kind of "variant" type that can hold any object along with its type information, and pass that "variant" type around. One example of such system is QVariant in Qt 4.
AFAIK implementation of variant type goes like this: there is some kind of table for every type variant supports, you register all types variant class must support in that table. Table provides functions for creating type, destroying type, (de)serializing type, and possibly information about amount of memory required by one object of the type. The table can contain optional information you want, and you can convert entire registration procedure into macros+template combo. As you can see, this is not something that is done automatically by compiler, but something that involves plenty of hassle and must be taken care of by programmer. Also, things get much more fun if program must be able to take types developed externally (in plugins, etc).
As a result of language restrictions, the better idea would be to avoid situations when you need to "return type" when possible - variant systems aren't exactly difficult, but they aren't much fun either, due to all necessary sanity checks and conversions. Example problem: if you pass a string in variant type to a function that is supposed to take a float, should this function attempt to convert string to float? If conversion fails, should it crash/throw exception, or assume that variable has default value? If there's default value for failed conversions, what should it be and how should it be passed? And so on. This isn't a rocket science, but it is quite annoying to deal with.
For example, you could get rid of "void*" (if function takes pointer as an argument, then I would assume that poitner can be NULL/0. So "void*" arguments aren't exactly a good idea). arguments in your functions and use templates to make compiler generate code your want for types you actually use in your program. If templates are not an option, then you need some kind of "variant" type (preferably developed by somebody else), ... or you could switch to another language that provides type information you need. You don't have to use C++, any tool that does the job will do. Relying on RTTI also isn't a perfect solution, becuase if you manage to pass a pointer to something that does NOT contain type information, you'll get a non-standard exception (__non_rtti_object
).
Upvotes: 1
Reputation: 7043
C++ is a statically typed language, thus types only really exist in a meaningful way at the compile time, not at runtime. At runtime the best C++ can give you is RTTI which provides you with things like dynamic_cast<> and typeid(), which are however limited to giving you information along the inheritance hierarchy only, i.e. if you have
class Base
class DerivedA : public Base
class DerivedB : public Base
and you have a Base*
or Base&
you can find out if it's a Base
, a DerivedA
or a DerivedB
. However in your case you only have a void*
, which is completely outside of any inheritance hierarchy and thus has no type information associated with it. Thus all typeid() will tell you is that you have a void*
, it won't tell you whatever type might hide behind it.
Furthermore a construct like:
print <typeid(myvector.at(2))> (myvector.at(2));
wouldn't work in C++ either, as the type for a template also needs to be known at compile time. Here however the type of .at(2)
would only be known at runtime.
So to solve your problem you have to do the type handling yourself. Meaning you have to store the type along with the object you want to store, which would look something like this:
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;
}
The boost::variant<>
class that visitor mentioned provides basically the above in a nicely packaged way.
Another thing worth to mention is decltype
, decltype
is new in C++11 standard and allows you to get the actual type of an object and thus you can write code like:
int a;
decltype(a) b;
Where b
gets the same type as a
, i.e. int
. This sounds exactly like what you want, but it is not, decltype()
has the same restrictions as before. It can only work when the type is already known at compile time, it can't do anything with types only known at runtime. Thus it will not work in your situation and is only really useful when doing some more complex template programming.
Long story short, use boost::variant<>
or write yourself a class that works in a similar way.
Upvotes: 1
Reputation: 1801
If you have a limited list of types you want to support, use boost::variant<int, float, const char*, ...>
instead of boost::any
(or void*
). Then you can define a visitor to call the correct instantiation of the print function.
#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);
}
}
With a void*
or a boost::any
, I don't think you can do better than use a long if-chain to test all supported types.
Upvotes: 1
Reputation: 577
PART 1: I'm not quite sure I fully understand your query, but have you looked at the header file <typeinfo>
? struct type_info
may be what you're looking for.
Upvotes: 0