0

システムのモジュールの 1 つは、さまざまなデータの転送に使用される JSON ベースのプロトコルを処理します。これにより、次のようなコードのほぼ 100 の小さなセクションが生成されます。

 /*
  * 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; }
 }

多くの場合、各ブロックの「肉」は 1 行または 2 行で、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 に答える