user3689849
user3689849

Reputation: 521

Any better way to get a variant value beside casting char*

In our legacy codes, we try to convert the primitive values from char* to some specific type like int, float, double, char(a bunch of char) and others.

The codes looks like following

char *data = db_data(process, retry_column);
int retry_time = *(int*)(data);
//......
data = db_data(process, user_name_column);
std::string user_name(data, std::strlen(data));

Any better way to get the variant data type without redesign the library?

If the only way is redesign the library, what kind of solution would you suggested without comprimise between performance and type safe(I want to get both in the same time), can boost::variant get the task done?

Upvotes: 1

Views: 163

Answers (3)

Christophe
Christophe

Reputation: 73500

If you want to have type-safety, you should without doubt choose boost::variant

However, this would require you to redesing db_data(), because the return wouldn't be a char* anymore.

So if there are too many things to be done, and you would like to start with a lighter change, I can propose you the following template and a specialisation for the case of strings:

template <typename T> 
inline T vdata(char* val) 
   { return *reinterpret_cast<T*>(val); }     

template<>
inline string vdata(char* val) 
   { return string(val); }  // special case for strings 

Attention: this template works by value. So its slightly different from your dereferenced casted pointer, because you cannot use it as an lvalue to assigne value to it (whereas *(int*)data = 12; would be valid)

With this definition you can change yor legacy code like this:

int retry_time = vdata<int>(data);
... 
std::string user_name = vdata<string>(data);

Of course you could make it even simpler by avoiding use of an interim data with:

template inline T vdb_data(int proc, char* col) // Attention: use the same parameter signature that the original, I didn't knew the real types you use { return vdata(db_data(proc, col)); }

But as some people pointed out, this solution is nice but will give you no type safety. For instance, it would be good in the string version, to make at least some consistency check (for example that it's null terminated, and that the length fits withing str::max_size().)

Upvotes: 1

Benjy Kessler
Benjy Kessler

Reputation: 7656

There is an inherent problem that there is no way of knowing if the bytes you get back are indeed the correct type. They are just a stream of 0s and 1s. So there is no way to make sure that the data in the database is the correct type (data size considerations aside). That being said you can avoid the casts in your client code by making db_data templated on the data type. It can then verify that the size is correct and then return the data casted to the correct type. This will look like this:

template<class DataType> DataType db_data();

Upvotes: 0

MSalters
MSalters

Reputation: 180020

The obvious solution is boost::variant.

Even so, it may be useful to give retry_column the type int_column_t so that the int db_data(..., int_column_t) overload is used.

Upvotes: 0

Related Questions