[英]How can I simplify this redundant C++ code?
有兩個現有的類,一個是SrcField
,它返回具體的類型值,另一個是union DSTField
,它定義了相應的數據類型。
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;
};
當我使用這兩個類時,應用程序如下所示。 這是多余的; 是否有任何簡化它的好方法,如模板,泛型編程等?
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;
}
}
使用std::variant
您的代碼將如下所示:
#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';
}
注:由於它是C ++ 17,對於以前的版本,我建議使用boost::variant
, boost::any
或僅標頭實現的Any
類(例如我使用基於一個這樣在我的項目)
你說你不能改變SrcField
,因此一個好的解決方案可能是訪問者的使用。 冗余代碼仍然存在,但它只出現一次。 看到這個:
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");
}
通過這種方式,您可以以一種簡單的方式使用這些值:
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);
}
在這種情況下,我使用了來自C ++ 17的if constexpr
功能。 另一種可能性是使用lambda重載
你可以在godbolt找到一個更完整的例子
注意:正如您所看到的,我根本沒有使用 DSTField
。 如果您確實需要使用DSTField
,可以使用類似的方法:
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;
...
}
可以用於類似的東西
DSTField dest;
setField(dest, 4.f);
其他說明:我將visitField
函數標記為constexpr ,但我無法確定您是否可以這樣使用。 實際上,如果SrcField::GetType
只能在運行時執行,那么visitField
將永遠不會在編譯時執行。
其他注意事項:我不知道這是否可能取決於您的代碼,但您必須記住,您無法確定signed char
是否為std::int8_t
(對於大多數其他類型,顯然)。 如果要使代碼在外部體系結構上按預期工作,則應使用固定寬度整數類型 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.