使用C 技术更少的代码
Less code using c++ techniques
我们有一个琐碎的C 任务,可以从HTTP端点解析一个大JSON文件,然后将值复制到自定义的本地类实例。(在下面的代码中,该实例是 obj
,带有seters setField1
, setField2
等)
此代码很"简单",但是JSON响应很大,并且导致一个非常大的C 文件可以执行相同的操作。但是,有一些要考虑的事情,即不同的类型和二传方法名称。以下是3个案例(一个int,一个布尔和双重),但我的代码包含至少50种类型的代码。我如何将代码现代化,使其易于错误并少采用较少的代码行?
行?if ( item_[i].HasMember("field1") && item_[i]["field1"].IsDouble()) {
double v = item_[i]["field1"].GetDouble();
if ( v < 0 )
throw CustomException("field1 value invalid");
obj.setField1(v);
} else {
throw CustomException("field1 missing or wrong data type");
}
if ( item_[i].HasMember("field2") && item_[i]["field2"].IsBool()) {
bool v = item_[i]["field2"].GetBool();
obj.setField2(v);
} else {
throw CustomException("field2 missing or wrong data type");
}
if ( item_[i].HasMember("field3") && item_[i]["field3"].IsInt()) {
int v = item_[i]["field3"].GetInt();
if ( v < 0 )
throw CustomException("field3 value invalid");
obj.setField3(v);
} else {
throw CustomException("field3 missing or wrong data type");
}
此序列化代码中最邪恶的事情是,恕我直言,复制和那些字符串标识符。在这里,我将张贴我的C 伪代码两美分(我不会使用Setters,但是可以轻松地扩展此想法以使用它们)。当然,此解决方案可能不符合您的代码,但这只是整体想法的概述。
首先,这是一些可序列化对象的声明:
class TestObject : public JsonSerializable
{
public:
TestObject()
{
// String field names are localized in a single place
// Here we create some sort of mapping from JSON to
// actual data.
addField("bool", &m_bool);
addField("int", &m_int);
addField("string", &m_string);
}
private:
bool m_bool
int m_int;
std::string m_string;
};
现在,让我们定义一个可从JSON文件处理对象的可添加jsonserializizizizable类:
class JsonSerializable
{
public:
// This method iterates all registered fields
// and tries to read them from a JSON
void load(const Json& json)
{
for (const auto& kv : m_fields)
{
kv.second->set(json[kv.first]);
}
}
protected:
// This method was used in a TestObject constructor
template<typename TValue>
void addField(const std::string& name, TValue* value)
{
m_fields[name] = new GenericField(value);
}
private:
// A map to store all fields to be loaded from JSON
// (can be a list, vector or any other favourite container)
std::map<std::string, GenericField*> m_fields;
};
终于但至少不是字段解析器接口:
// An interface that is exposed to JsonSerializable that hides
// a type-specific serialization process.
class Field
{
public:
// Contains just one method to set a field from a JSON value.
virtual void set(const JsonValue& value) = 0;
};
// Generic type-specific implementation
template<typename TValue>
class GenericField : public Field
{
public:
// Each field contains a pointer to a field, but here you can
// also use pointer to a method or std::function to add setters.
GenericField(TValue* value)
: m_value(value)
{
}
// And here is an actual serialization code, that extracts a
// value from a JSON and writes to a pointed chunk of memory.
virtual void set(const JsonValue& value)
{
*m_value = value.as<TValue>();
}
private:
TValue* m_value;
};
因此,这里的基本思想是通过隐藏字段接口后面的实际序列化代码来消除代码重复,并将字符串标识符定位在一个位置 - 在一个可序列化对象的构造函数中。
希望这会有所帮助。
我有一个具有此接口的JSON解析器
int err = 0;
JSONParser jparser(json_as_stdstring);
x = jparser.getDouble("fielda, &err);
if(err)
/* we have an error */
但是,错误是粘的。因此代码看起来像这样
int err = 0;
JSONParser jparser(json_as_stdstring);
Myclass myclass; // object to fill;
myclass.x = jparser.getDouble("fielda", &err);
myclass.name = jparser.getString("name", &err);
myclass.id = jparser.getInteger("id" &err);
if(err)
/* we have an error */
它使复杂性无法解析,在常见情况下,JSON中的任何错误都会使整个转换无效。如果您可以忍受不良数据或缺少数据,那么您当然可以处理并将ERR重置为0。
首先,我们需要检查字段类型并根据其C 类型获取其值的方法。
template <typename T>
bool Is(const Field &field) { return false; }
template <> bool Is<bool>(const Field &field) { return field.IsBool(); }
template <> bool Is<double>(const Field &field) { return field.IsDouble(); }
template <> bool Is<int>(const Field &field) { return field.IsInt(); }
template <typename T>
T Get(const Field &field) { throw 0; }
template <> T Get<bool>(const Field &field) { return field.GetBool(); }
template <> T Get<double>(const Field &field) { return field.GetDouble(); }
template <> T Get<int>(const Field &field) { return field.GetInt(); }
在上面,我假设您的类型有限,并且为每个功能模板专门为每个功能模板提供了很大的意义。请注意,当您请求特定字段时,该字段是JSON解析器返回的任何类型。
现在我们可以构建一个通用提取器。我注意到您的示例在此步骤中也进行了验证,但我将把它分开。注意jsonthing是什么类型的JSON对象(与原始帖子中的item_[i]
相同)。
template <typename T>
T Extract(const JSONThing &json, const char * field_name) {
if (!json.HasMember(field_name)) throw CustomException("missing field")
const Field &field = json[field_name];
if (!Is<T>(field)) throw CustomException("wrong type");
return Get<T>(field);
}
对于验证,您可以使用函数(或功能模板),如果输入值有效,则可以返回输入值。诚然,这是有点人为的,但是它使对象填充很简单。
template <typename T>
const T &Nonnegative(const T &value) {
if (value < static_cast<T>(0)) throw CustomException("invalid value");
return value;
}
现在您可以这样填充对象:
const auto &json = item_[i];
obj.setField1(Nonnegative(Extract<double>(json, "Field1")));
obj.setField2(Extract<bool>(json, "Field2"));
obj.setField3(Nonnegative(Extract<int>(json, "Field3")));
// ...
我认为这很可读,它几乎消除了所有重复,因此几乎没有机会犯错。如果您需要在自定义异常中进行更多详细信息,则必须做更多的工作。
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 代码在main()中运行,但在函数中出现错误
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 编译包含字符串的代码时遇到问题
- 我在c++代码中生成了一个运行时#3异常
- 如何在linux终端中同时编译和运行c++代码
- 为cl.exe(Visual Studio代码)指定命令行C++版本
- 在Linux for Windows上编译C++代码时出错
- 我的字符计数代码计算错误.为什么
- 孤立代码块在结构中引发异常
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 此代码是否违反一个定义规则
- 哪种排序技术在代码下方
- 使用C 技术更少的代码
- 用于在 C++ 中管理特定于平台的代码的内联命名空间技术
- 混合代码(本机代码、托管代码):它(技术上)如何互操作
- 此C++代码在技术上会发生什么
- 用于防止代码中死锁的锁定策略和技术