如何有效地将 if 和 else 用于过滤构造?
Posted
技术标签:
【中文标题】如何有效地将 if 和 else 用于过滤构造?【英文标题】:How to effectively use if and else for a filtering construct? 【发布时间】:2013-06-23 17:10:14 【问题描述】:要解析从 javascript 获得的函数参数,我需要执行大量检查。例如,一个函数可能需要一个对象作为参数,在 JavaScript 中看起来像这样。
Fullscreen: [ 'bool', false ],
Size: [ 'Vector2u', 800, 600 ],
Title: [ 'string', 'Hello World' ],
// more properties...
在 C++ 中,我通过遍历所有键并检查它们来解析它。如果其中一项检查失败,则应打印错误消息并跳过此键值对。这就是我目前的实现方式。我希望您不会因某些特定于引擎的调用而分心。
ModuleSettings *module = (ModuleSettings*)HelperScript::Unwrap(args.Data());
if(args.Length() < 1 || !args[0]->IsObject())
return v8::Undefined();
v8::Handle<v8::Object> object = args[0]->ToObject();
auto stg = module->Global->Get<Settings>("settings");
v8::Handle<v8::Array> keys = object->GetPropertyNames();
for(unsigned int i = 0; i < keys->Length(); ++i)
string key = *v8::String::Utf8Value(keys->Get(i));
if(!object->Get(v8::String::New(key.c_str()))->IsArray())
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
continue;
v8::Handle<v8::Array> values = v8::Handle<v8::Array>::Cast(object->Get(v8::String::New(key.c_str())));
if(!values->Has(0) || !values->Get(0)->IsString())
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
continue;
string type = *v8::String::Utf8Value(values->Get(0));
if(type == "bool")
if(!values->Has(1) || !values->Get(1)->IsBoolean())
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
continue;
stg->Set<bool>(key, values->Get(1)->BooleanValue());
else if(type == "Vector2u")
if(!values->Has(1) || !values->Has(2) || !values->Get(1)->IsUint32(), !values->Get(2)->IsUint32())
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
continue;
stg->Set<Vector2u>(key, Vector2u(values->Get(1)->Uint32Value(), values->Get(2)->Uint32Value()));
else if(type == "string")
if(!values->Has(1) || !values->Get(1)->IsString())
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
continue;
stg->Set<string>(key, *v8::String::Utf8Value(values->Get(1)));
如您所见,我定义了在每个过滤器检查失败时发生的时间。
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
continue;
我只想写一次,但我只能想出一个使用goto
的方法,我想阻止它。是否有更好的选择来重构 if
else
构造?
【问题讨论】:
为什么这个标签是 C++? @MatsPetersson 如您所见,该函数是用原生 C++ 编写的。但它只能从 JavaScript 调用。如果 JavaScript 执行它,我使用的脚本引擎会将args
对象中的参数发送到我的 C++ 函数。所以我需要验证args
,因为JavaScript 对函数参数非常宽松。我的问题是关于这个 C++ 部分。
如果你想扁平化 if else 语句使用表格。这有点像编译器对函数的处理。
【参考方案1】:
我想我会从一组小类开始对每种类型进行验证:
auto v_string = [](v8::Handle<v8::Array> const &v)
return v->Has(1) && v->Get(1)->IsString();
auto v_Vector2u = [](v8::Handle<v8::Array> const &v)
return v->Has(1) && v->Has(2) &&
v->Get(1)->IsUint32() && v->Get(2)->IsUint32();
// ...
然后我会创建一个从类型名称到该类型验证器的映射:
std::map<std::string, decltyp(v_string)> verifiers;
verifiers["string"] = v_string;
verifiers["Vector2u"] = v_Vector2u;
// ...
然后要验证一个类型,你会使用这样的东西:
// find the verifier for this type:
auto verifier = verifiers.find(type);
// If we can't find a verifier, reject the data:
if (verifier == verifiers.end())
HelperDebug::Fail("script", "unknown type: " + type);
// found the verifier -- verify the data:
if (!verifier->second(values))
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
【讨论】:
@CaptainObvlious:谢谢——但我不吃 Ho Ho 的。 :-)【参考方案2】:在这种情况下,一种非常常用的方法是使用宏。即在函数之前或函数中的#defined 和函数结束时的#undef-ed。因为你需要continue
,所以其他的方法都差不多了。
goto 也是一个解决方案,但对于这个特定的示例,我不会使用它。
一种更轻松的方法是至少将失败调用放入一个函数或 lambda 中,这仍然给您留下 continue 部分。
我可能会创建一个将过滤器表达式作为参数的宏,留下类似的代码。
if(type == "bool")
ENSURE(values->Has(1) && values->Get(1)->IsBoolean());
stg->Set<bool>(key, values->Get(1)->BooleanValue());
...
【讨论】:
有没有办法重组if
else
结构?
在 C++ 中,总有办法,甚至太多,对于这种多样性,我估计替代方案会消耗更多的代码,而不会提高可读性。如果真正的问题不是这么简单,那可能会将平衡转移到其他地方
在我的实际应用程序中,我必须检查大约 15 种可能的类型。我只能想办法使用goto
...
有趣的不是类型的数量,而是检查和 after-if 操作的性质。专注于目标,即 TRY SPOT 和可读性。其余的都是次要的。如果你删除了所有重复和代码读取自然,你就完成了,而不是镀金做一些有用的事情;)
我同意。如果检查失败,则无需处理更多逻辑。但我想删除重复。我想摆脱经常出现的这两个调用(或包装一些调用的函数)。以上是关于如何有效地将 if 和 else 用于过滤构造?的主要内容,如果未能解决你的问题,请参考以下文章