Allow conversions while calling chaiscript funcs

* This puts ChaiScript funcs more on even footings with
   C++ defined funcs
 * Minor performance hit (0.5%)
This commit is contained in:
Jason Turner
2016-12-06 13:05:17 -07:00
parent 92c2ade1cd
commit cee57f998a
3 changed files with 110 additions and 25 deletions

View File

@@ -72,10 +72,51 @@ namespace chaiscript
return m_types == t_rhs.m_types; return m_types == t_rhs.m_types;
} }
bool match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const std::vector<Boxed_Value> convert(std::vector<Boxed_Value> vals, const Type_Conversions_State &t_conversions) const
{ {
if (!m_has_types) { return true; } for (size_t i = 0; i < vals.size(); ++i)
if (vals.size() != m_types.size()) { return false; } {
const auto &name = m_types[i].first;
if (!name.empty()) {
const auto &bv = vals[i];
if (!bv.get_type_info().bare_equal(m_doti))
{
const auto &ti = m_types[i].second;
if (!ti.is_undef())
{
if (!bv.get_type_info().bare_equal(ti)) {
if (t_conversions->converts(ti, bv.get_type_info())) {
try {
// We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it
// either way, we are not responsible if it doesn't work
vals[i] = t_conversions->boxed_type_conversion(m_types[i].second, t_conversions.saves(), vals[i]);
} catch (...) {
try {
// try going the other way
vals[i] = t_conversions->boxed_type_down_conversion(m_types[i].second, t_conversions.saves(), vals[i]);
} catch (const chaiscript::detail::exception::bad_any_cast &) {
throw exception::bad_boxed_cast(bv.get_type_info(), *m_types[i].second.bare_type_info());
}
}
}
}
}
}
}
}
return vals;
}
// first result: is a match
// second result: needs conversions
std::pair<bool, bool> match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const
{
bool needs_conversion = false;
if (!m_has_types) { return std::make_pair(true, needs_conversion); }
if (vals.size() != m_types.size()) { return std::make_pair(false, needs_conversion); }
for (size_t i = 0; i < vals.size(); ++i) for (size_t i = 0; i < vals.size(); ++i)
{ {
@@ -87,25 +128,31 @@ namespace chaiscript
{ {
try { try {
const Dynamic_Object &d = boxed_cast<const Dynamic_Object &>(bv, &t_conversions); const Dynamic_Object &d = boxed_cast<const Dynamic_Object &>(bv, &t_conversions);
return name == "Dynamic_Object" || d.get_type_name() == name; if (!(name == "Dynamic_Object" || d.get_type_name() == name)) {
return std::make_pair(false, false);
}
} catch (const std::bad_cast &) { } catch (const std::bad_cast &) {
return false; return std::make_pair(false, false);
} }
} else { } else {
const auto &ti = m_types[i].second; const auto &ti = m_types[i].second;
if (!ti.is_undef()) if (!ti.is_undef())
{ {
if (!bv.get_type_info().bare_equal(ti)) { if (!bv.get_type_info().bare_equal(ti)) {
return false; if (!t_conversions->converts(ti, bv.get_type_info())) {
return std::make_pair(false, false);
} else {
needs_conversion = true;
}
} }
} else { } else {
return false; return std::make_pair(false, false);
} }
} }
} }
} }
return true; return std::make_pair(true, needs_conversion);
} }
const std::vector<std::pair<std::string, Type_Info>> &types() const const std::vector<std::pair<std::string, Type_Info>> &types() const
@@ -320,8 +367,7 @@ namespace chaiscript
bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const override bool call_match(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const override
{ {
return (m_arity < 0 || (vals.size() == size_t(m_arity) && m_param_types.match(vals, t_conversions))) return call_match_internal(vals, t_conversions).first;
&& test_guard(vals, t_conversions);
} }
@@ -353,6 +399,26 @@ namespace chaiscript
} }
} }
// first result: is a match
// second result: needs conversions
std::pair<bool, bool> call_match_internal(const std::vector<Boxed_Value> &vals, const Type_Conversions_State &t_conversions) const
{
const auto comparison_result = [&](){
if (m_arity < 0) {
return std::make_pair(true, false);
} else if (vals.size() == size_t(m_arity)) {
return m_param_types.match(vals, t_conversions);
} else {
return std::make_pair(false, false);
}
}();
return std::make_pair(
comparison_result.first && test_guard(vals, t_conversions),
comparison_result.second
);
}
private: private:
static std::vector<Type_Info> build_param_type_list(const Param_Types &t_types) static std::vector<Type_Info> build_param_type_list(const Param_Types &t_types)
{ {
@@ -371,7 +437,10 @@ namespace chaiscript
return types; return types;
} }
protected:
Param_Types m_param_types; Param_Types m_param_types;
private:
Proxy_Function m_guard; Proxy_Function m_guard;
AST_NodePtr m_parsenode; AST_NodePtr m_parsenode;
}; };
@@ -402,9 +471,14 @@ namespace chaiscript
protected: protected:
Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const override Boxed_Value do_call(const std::vector<Boxed_Value> &params, const Type_Conversions_State &t_conversions) const override
{ {
if (call_match(params, t_conversions) && test_guard(params, t_conversions)) const auto match_results = call_match_internal(params, t_conversions);
if (match_results.first)
{ {
return m_f(params); if (match_results.second) {
return m_f(m_param_types.convert(params, t_conversions));
} else {
return m_f(params);
}
} else { } else {
throw exception::guard_error(); throw exception::guard_error();
} }

View File

@@ -397,28 +397,39 @@ namespace chaiscript
template<typename To> template<typename To>
Boxed_Value boxed_type_conversion(Conversion_Saves &t_saves, const Boxed_Value &from) const Boxed_Value boxed_type_conversion(Conversion_Saves &t_saves, const Boxed_Value &from) const
{ {
try { return boxed_type_conversion(user_type<To>(), t_saves, from);
Boxed_Value ret = get_conversion(user_type<To>(), from.get_type_info())->convert(from);
if (t_saves.enabled) { t_saves.saves.push_back(ret); }
return ret;
} catch (const std::out_of_range &) {
throw exception::bad_boxed_dynamic_cast(from.get_type_info(), typeid(To), "No known conversion");
} catch (const std::bad_cast &) {
throw exception::bad_boxed_dynamic_cast(from.get_type_info(), typeid(To), "Unable to perform dynamic_cast operation");
}
} }
template<typename From> template<typename From>
Boxed_Value boxed_type_down_conversion(Conversion_Saves &t_saves, const Boxed_Value &to) const Boxed_Value boxed_type_down_conversion(Conversion_Saves &t_saves, const Boxed_Value &to) const
{
return boxed_type_down_conversion(user_type<From>(), t_saves, to);
}
Boxed_Value boxed_type_conversion(const Type_Info &to, Conversion_Saves &t_saves, const Boxed_Value &from) const
{ {
try { try {
Boxed_Value ret = get_conversion(to.get_type_info(), user_type<From>())->convert_down(to); Boxed_Value ret = get_conversion(to, from.get_type_info())->convert(from);
if (t_saves.enabled) { t_saves.saves.push_back(ret); } if (t_saves.enabled) { t_saves.saves.push_back(ret); }
return ret; return ret;
} catch (const std::out_of_range &) { } catch (const std::out_of_range &) {
throw exception::bad_boxed_dynamic_cast(to.get_type_info(), typeid(From), "No known conversion"); throw exception::bad_boxed_dynamic_cast(from.get_type_info(), *to.bare_type_info(), "No known conversion");
} catch (const std::bad_cast &) { } catch (const std::bad_cast &) {
throw exception::bad_boxed_dynamic_cast(to.get_type_info(), typeid(From), "Unable to perform dynamic_cast operation"); throw exception::bad_boxed_dynamic_cast(from.get_type_info(), *to.bare_type_info(), "Unable to perform dynamic_cast operation");
}
}
Boxed_Value boxed_type_down_conversion(const Type_Info &from, Conversion_Saves &t_saves, const Boxed_Value &to) const
{
try {
Boxed_Value ret = get_conversion(to.get_type_info(), from)->convert_down(to);
if (t_saves.enabled) { t_saves.saves.push_back(ret); }
return ret;
} catch (const std::out_of_range &) {
throw exception::bad_boxed_dynamic_cast(to.get_type_info(), *from.bare_type_info(), "No known conversion");
} catch (const std::bad_cast &) {
throw exception::bad_boxed_dynamic_cast(to.get_type_info(), *from.bare_type_info(), "Unable to perform dynamic_cast operation");
} }
} }

View File

@@ -1226,7 +1226,7 @@ namespace chaiscript
if (dispatch::Param_Types( if (dispatch::Param_Types(
std::vector<std::pair<std::string, Type_Info>>{Arg_List_AST_Node<T>::get_arg_type(catch_block->children[0], t_ss)} std::vector<std::pair<std::string, Type_Info>>{Arg_List_AST_Node<T>::get_arg_type(catch_block->children[0], t_ss)}
).match(std::vector<Boxed_Value>{t_except}, t_ss.conversions())) ).match(std::vector<Boxed_Value>{t_except}, t_ss.conversions()).first)
{ {
t_ss.add_object(name, t_except); t_ss.add_object(name, t_except);