0

该系统的一个模块处理基于 JSON 的协议,该协议用于传输各种数据。这导致了近百个如下所示的小段代码:

 /*
  * Data package Foo reports Fooness level
  */
 if(root.isMember("foo") && root["foo"].isInt())
 {
     int foo = root["foo"].asInt();
     // do things with foo
 }


 /*
  * Data package Bar gives ID number and name of a newly arrived bar.
  */
 if(root.isMember("bar") && root["bar"].isObject())
 {
     JSON::Value bar = root["bar"];

     if(bar.isMember("baz") && bar["baz"].isString()
     && bar.isMember("buzz") && bar["buzz"].isInt())
     {
          std::string baz = bar["baz"].asString();
          int buzz = bar["buzz"].asInt();
         // do things with baz and buzz

     }
     else{ err["bar"] = argument_error; }
 }

不仅每个块的“肉”通常是一两行,大约有 10 行参数验证,这会导致无数的复制粘贴错误和可维护性问题(一个键更改名称,它必须在大约 6 个地方更改)。

在没有所有代码重复的情况下,您将如何重塑这些模式以完成它们所做的事情?(注意,所有主键和少数子键都是可选的,大多数子键是强制性的。)

4

2 回答 2

2

您没有列出诸如 之类的类型bar,所以我仅Bar用于该类型。

你可以给自己一些辅助方法,利用状态码或异常,这取决于你期望失败的常见程度。

bool get_string(std::string& result, const Bar& bar, const char* name)
{
  if(bar.isMember(name) && bar[name].isString())
  {
    result = bar[name].asString();
    return true;
  }
  return false;
}

// and similarly
bool get_int(int& result, const Bar& bar, const char* name)
{
  if(bar.isMember(name) && bar[name].isInt())
  {
    result = bar[name].asInt();
    return true;
  }
  return false;
}

然后你可以使用它:

JSON::Value bar;
std::string baz;
int buzz;
if(get_object(bar, root, "bar"))
{
  if (get_string(baz, bar, "baz")
      && get_int(buzz, bar, "buzz"))
  {
     // do things with baz and buzz
  }
  else{ err["bar"] = argument_error; }
}

这稍微干净一些,但不是突飞猛进。如果您期望您正在寻找的东西存在,并且不太可能出现故障,那么我们可以使用异常。我们也可以用模板全力以赴:

// set up template forms to check types
template<typename T> bool is_a(const Bar& b);
template<> bool is_a<std::string>(const Bar& b) { return b.isString(); }
template<> bool is_a<int>        (const Bar& b) { return b.isInt();    }
template<> bool is_a<JSON::Value>(const Bar& b) { return b.isObject(); }

// templates to extract as a type
template<typename T> T as_type(const Bar& b);
template<> std::string as_type<std::string>(const Bar& b) { return b.asString(); }
template<> int         as_type<int>        (const Bar& b) { return b.asInt();    }
template<> JSON::Value as_type<JSON::Value>(const Bar& b) { return b.asObject(); }

// the one extraction method
template<typename T>
T get(const Bar& bar, const char* name)
{
  if ( ! bar.isMember(name))  throw std::runtime_error("not a member");
  if ( ! is_a<T>(bar[name]))  throw std::runtime_error("wrong type");
  return as_type<T>(bar[name]);
}

// and now we use it
try
{
  JSON::Value bar = get<JSON::Value>(root, "bar");
  std::string baz = get<std::string>(bar, "baz");
  int buzz = get<int>(bar, "buzz");
  // do things with baz and buzz
}
catch (const std::runtime_error& exc)
{
  err["bar"] = argument_error;
}

设置需要更多方法,但现在使用它非常简单和干净。

于 2011-08-01T23:28:53.673 回答
1

由于您想自动命名标识符,因此您基本上只能使用预处理器。你可以尝试这样的事情:

#define FOO(var, name, cpp, json) cpp name((var.isMember(#name) && var[#name].is##json) ? var[#name].as##json() : "[ERROR in " #name "]")
int main()
{
  FOO(bar, foo, std::string, String);

  // translates into:
  std::string foo((bar.isMember("foo") && bar["foo"].isString) ? bar["foo"].asString() : "[ERROR in " "foo" "]");
}
于 2011-08-01T22:34:08.600 回答