There are two existing classes, one is SrcField
which returns the concrete type value, and the other is a union DSTField
, that defines the corresponding data type.
class SrcField
{
public:
signed char GetInt8();
unsigned char GetUInt8();
short GetInt16();
unsigned short GetUInt16();
int GetInt32();
unsigned int GetUInt32();
float GetFloat();
double GetDouble();
bool GetBool();
DataType GetType();
private:
DataType m_type;
DSTField m_data;
};
union DSTField
{
signed char m_int8;
unsigned char m_uint8;
short m_int16;
unsigned short m_uint16;
int m_int32;
unsigned int m_uint32;
float m_float;
double m_double;
bool m_bool;
};
When I use both classes, the application is as below. It's very redundant; is there any good way to simplify it, such as templates, generic programming, etc?
int main()
{
SrcField sf;
DSTField df;
switch(sf.GetType())
{
case TYPE_INT8:
df.m_int8 = sf.GetInt8();
break;
case TYPE_UINT8:
df.m_uint8 = sf.GetUInt8();
break;
case TYPE_INT16:
df.m_int16 = sf.GetInt16();
break;
case TYPE_UINT16:
df.m_uint16 = sf.GetUInt16();
break;
case TYPE_INT32:
df.m_int32 = sf.GetInt32();
break;
case TYPE_UINT32:
df.m_uint32 = sf.GetUInt32();
break;
case TYPE_FLOAT:
df.m_float = sf.GetFloat();
break;
case TYPE_DOUBLE:
df.m_double = sf.GetDouble();
break;
case TYPE_BOOL:
df.m_bool = sf.GetBool();
break;
default:
break;
}
}
Using std::variant
your code would look like this:
#include <iostream>
#include <variant>
typedef std::variant<
signed char,
unsigned char,
short,
unsigned short,
int,
unsigned int,
float,
double,
bool
> SrcField, DSTField;
int main()
{
SrcField sf(97.0f);
DSTField df;
df = sf;
if(auto pval = std::get_if<float>(&df))
std::cout << "variant value: " << *pval << '\n';
else
std::cout << "failed to get value!" << '\n';
}
Note: Since it's c++17, for previous versions I recommend to use boost::variant
, boost::any
or a header-only implementation of Any
class (for example I use one based on this in my project)
You said that you cannot change SrcField
, therefore a good solution could be the use of a visitor. The redundant code is still there, but it is present only once. See this:
template<typename Visitor>
constexpr void
visitField(Visitor&& visitor, SrcField& field)
{
switch(field.GetType())
{
case TYPE_INT8:
visitor(field.GetInt8());
break;
case TYPE_UINT8:
visitor(field.GetUInt8());
break;
....
default:
throw std::runtime_error("invalid type");
}
In this way you are able to use the values in a simple way:
int main()
{
SrcField field;
visitField([](auto value) {
if constexpr(std::is_same<decltype(value), double>::value)
std::cout << "Hey, double here!\n";
else if constexpr(std::is_same<decltype(value), bool>::value)
std::cout << "True or false?\n";
else
std::cout << "Other types\n";
std::cout << value << '\n';
}, field);
}
In this case I used the if constexpr
capability from C++17. Another possibility use a lambda overload
You can find a more complete example here on godbolt
Note: As you can see, I did not use DSTField
at all. If you really need to use DSTField
, you can use a similar approach:
template<typename T>
constexpr void
setField(DSTField& dstField, T value)
{
static_assert(std::is_arithmetic<T>::value,
"value must be an arithmetic type");
if constexpr(std::is_same<T, signed char>::value)
dstField.m_int8 = value;
else if constexpr(std::is_same<T, unsigned char>::value)
dstField.m_uint8 = value;
...
}
which can be used with something like
DSTField dest;
setField(dest, 4.f);
Other note: I marked the visitField
function as constexpr , but I cannot be sure if you can use in that way. Indeed, if SrcField::GetType
can only be executed at runtime , visitField
will never be executed at compile time .
Other other note: I don't know if this could depend on your code or not, but you have to keep in mind that you cannot be sure that signed char
is a std::int8_t
(as for most of the other types, obviously). You should use fixed width integer types if you want to make your code work as expected on foreign architectures.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.