Permalink
Browse files

Add ability to call functions requiring arithmetic value conversions

 - Conversions are only attempted on a dispatch
 - Conversions are only attempted after a normal dispatch has failed
 - Conversions are only attempted if exactly one function matches
   the signature of the parameters passed in - excluding the mismatched
   arithmetic parameters
 - This feature should not be relied on in performance critical code
   overhead is added for each function call that requires a conversion
   to execute, see the tests performed above.
  • Loading branch information...
1 parent f24d376 commit 0ea8931b213b623fc3a48038ae81bc53d95c8dfd @lefticus lefticus committed Nov 28, 2012
View
@@ -212,6 +212,10 @@ if(BUILD_TESTING)
target_link_libraries(integer_literal_test ${LIBS})
add_test(NAME Integer_Literal_Test COMMAND integer_literal_test)
+ add_executable(arithmetic_conversions_test unittests/arithmetic_conversions_test.cpp)
+ target_link_libraries(arithmetic_conversions_test ${LIBS})
+ add_test(NAME Arithmetic_Conversions_Test COMMAND arithmetic_conversions_test)
+
if (MULTITHREAD_SUPPORT_ENABLED)
add_executable(multithreaded_test unittests/multithreaded_test.cpp)
target_link_libraries(multithreaded_test ${LIBS})
@@ -332,6 +332,46 @@ namespace chaiscript
validate_boxed_number(bv);
}
+ Boxed_Number get_as(const Type_Info &inp_) const
+ {
+ if (inp_.bare_equal_type_info(typeid(int))) {
+ return Boxed_Number(get_as<int>());
+ } else if (inp_.bare_equal_type_info(typeid(double))) {
+ return Boxed_Number(get_as<double>());
+ } else if (inp_.bare_equal_type_info(typeid(float))) {
+ return Boxed_Number(get_as<float>());
+ } else if (inp_.bare_equal_type_info(typeid(long double))) {
+ return Boxed_Number(get_as<long double>());
+ } else if (inp_.bare_equal_type_info(typeid(char))) {
+ return Boxed_Number(get_as<char>());
+ } else if (inp_.bare_equal_type_info(typeid(unsigned int))) {
+ return Boxed_Number(get_as<unsigned int>());
+ } else if (inp_.bare_equal_type_info(typeid(long))) {
+ return Boxed_Number(get_as<long>());
+ } else if (inp_.bare_equal_type_info(typeid(unsigned long))) {
+ return Boxed_Number(get_as<unsigned long>());
+ } else if (inp_.bare_equal_type_info(typeid(boost::int8_t))) {
+ return Boxed_Number(get_as<boost::int8_t>());
+ } else if (inp_.bare_equal_type_info(typeid(boost::int16_t))) {
+ return Boxed_Number(get_as<boost::int16_t>());
+ } else if (inp_.bare_equal_type_info(typeid(boost::int32_t))) {
+ return Boxed_Number(get_as<boost::int32_t>());
+ } else if (inp_.bare_equal_type_info(typeid(boost::int64_t))) {
+ return Boxed_Number(get_as<boost::int64_t>());
+ } else if (inp_.bare_equal_type_info(typeid(boost::uint8_t))) {
+ return Boxed_Number(get_as<boost::uint8_t>());
+ } else if (inp_.bare_equal_type_info(typeid(boost::uint16_t))) {
+ return Boxed_Number(get_as<boost::uint16_t>());
+ } else if (inp_.bare_equal_type_info(typeid(boost::uint32_t))) {
+ return Boxed_Number(get_as<boost::uint32_t>());
+ } else if (inp_.bare_equal_type_info(typeid(boost::uint64_t))) {
+ return Boxed_Number(get_as<boost::uint64_t>());
+ } else {
+ throw boost::bad_any_cast();
+ }
+
+ }
+
template<typename Target> Target get_as() const
{
const Type_Info &inp_ = bv.get_type_info();
@@ -1109,6 +1109,14 @@ namespace chaiscript
vec.push_back(t_f);
std::stable_sort(vec.begin(), vec.end(), &function_less_than);
func_objs[t_name] = Proxy_Function(new Dispatch_Function(vec));
+ } else if (t_f->has_arithmetic_param()) {
+ // if the function is the only function but it also contains
+ // arithmetic operators, we must wrap it in a dispatch function
+ // to allow for automatic arithmetic type conversions
+ std::vector<Proxy_Function> vec;
+ vec.push_back(t_f);
+ funcs.insert(std::make_pair(t_name, vec));
+ func_objs[t_name] = Proxy_Function(new Dispatch_Function(vec));
} else {
std::vector<Proxy_Function> vec;
vec.push_back(t_f);
@@ -85,6 +85,11 @@ namespace chaiscript
virtual bool operator==(const Proxy_Function_Base &) const = 0;
virtual bool call_match(const std::vector<Boxed_Value> &vals) const = 0;
+ bool has_arithmetic_param() const
+ {
+ return m_has_arithmetic_param;
+ }
+
virtual std::vector<boost::shared_ptr<const Proxy_Function_Base> > get_contained_functions() const
{
return std::vector<boost::shared_ptr<const Proxy_Function_Base> >();
@@ -117,23 +122,8 @@ namespace chaiscript
virtual std::string annotation() const = 0;
- protected:
- virtual Boxed_Value do_call(const std::vector<Boxed_Value> &params) const = 0;
-
- Proxy_Function_Base(const std::vector<Type_Info> &t_types)
- : m_types(t_types)
- {
- }
-
- virtual bool compare_first_type(const Boxed_Value &bv) const
+ static bool compare_type_to_param(const Type_Info &ti, const Boxed_Value &bv)
{
- const std::vector<Type_Info> &types = get_param_types();
-
- if (types.size() < 2)
- {
- return true;
- }
- const Type_Info &ti = types[1];
if (ti.is_undef()
|| ti.bare_equal(user_type<Boxed_Value>())
|| (!bv.get_type_info().is_undef()
@@ -150,6 +140,36 @@ namespace chaiscript
return false;
}
}
+ protected:
+ virtual Boxed_Value do_call(const std::vector<Boxed_Value> &params) const = 0;
+
+ Proxy_Function_Base(const std::vector<Type_Info> &t_types)
+ : m_types(t_types), m_has_arithmetic_param(false)
+ {
+ for (int i = 1; i < m_types.size(); ++i)
+ {
+ if (m_types[i].is_arithmetic())
+ {
+ m_has_arithmetic_param = true;
+ return;
+ }
+ }
+
+ }
+
+ virtual bool compare_first_type(const Boxed_Value &bv) const
+ {
+ const std::vector<Type_Info> &types = get_param_types();
+
+ if (types.size() < 2)
+ {
+ return true;
+ }
+
+ const Type_Info &ti = types[1];
+ return compare_type_to_param(ti, bv);
+
+ }
bool compare_types(const std::vector<Type_Info> &tis, const std::vector<Boxed_Value> &bvs) const
{
@@ -170,6 +190,8 @@ namespace chaiscript
}
std::vector<Type_Info> m_types;
+ bool m_has_arithmetic_param;
+
};
}
@@ -605,6 +627,92 @@ namespace chaiscript
namespace dispatch
{
+ namespace detail
+ {
+ template<typename FuncType>
+ bool types_match_except_for_arithmetic(const FuncType &t_func, const std::vector<Boxed_Value> &plist)
+ {
+ if (t_func->get_arity() != plist.size())
+ {
+ return false;
+ }
+
+ const std::vector<Type_Info> &types = t_func->get_param_types();
+
+ assert(plist.size() == types.size() - 1);
+
+ for (int i = 0; i < plist.size(); ++i)
+ {
+ if (Proxy_Function_Base::compare_type_to_param(types[i+1], plist[i])
+ || (types[i+1].is_arithmetic() && plist[i].get_type_info().is_arithmetic()))
+ {
+ // types continue to match
+ } else {
+ return false;
+ }
+ }
+
+ // all types match
+ return true;
+ }
+
+ template<typename InItr>
+ Boxed_Value dispatch_with_conversions(InItr begin, const InItr &end, const std::vector<Boxed_Value> &plist)
+ {
+ InItr orig(begin);
+
+ InItr matching_func(end);
+
+ while (begin != end)
+ {
+ if (types_match_except_for_arithmetic(*begin, plist))
+ {
+ if (matching_func == end)
+ {
+ matching_func = begin;
+ } else {
+ // More than one function matches, not attempting
+ throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(orig, end));
+ }
+ }
+
+ ++begin;
+ }
+
+ if (matching_func == end)
+ {
+ // no appropriate function to attempt arithmetic type conversion on
+ throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(orig, end));
+ }
+
+
+ std::vector<Boxed_Value> newplist;
+ const std::vector<Type_Info> &tis = (*matching_func)->get_param_types();
+
+ for (int i = 0; i < plist.size(); ++i)
+ {
+ if (tis[i+1].is_arithmetic()
+ && plist[i].get_type_info().is_arithmetic()) {
+ newplist.push_back(Boxed_Number(plist[i]).get_as(tis[i+1]).bv);
+ } else {
+ newplist.push_back(plist[i]);
+ }
+ }
+
+ try {
+ return (*(*matching_func))(newplist);
+ } catch (const exception::bad_boxed_cast &) {
+ //parameter failed to cast
+ } catch (const exception::arity_error &) {
+ //invalid num params
+ } catch (const exception::guard_error &) {
+ //guard failed to allow the function to execute
+ }
+
+ throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(orig, end));
+
+ }
+ }
/**
* Take a vector of functions and a vector of parameters. Attempt to execute
@@ -634,7 +742,7 @@ namespace chaiscript
++begin;
}
- throw exception::dispatch_error(plist, std::vector<Const_Proxy_Function>(orig, end));
+ return detail::dispatch_with_conversions(orig, end, plist);
}
/**
@@ -99,6 +99,12 @@ namespace chaiscript
|| (ti.m_bare_type_info && m_bare_type_info && *ti.m_bare_type_info == *m_bare_type_info);
}
+ bool bare_equal_type_info(const std::type_info &ti) const
+ {
+ return m_bare_type_info != 0
+ && (*m_bare_type_info) == ti;
+ }
+
bool is_const() const { return m_is_const; }
bool is_reference() const { return m_is_reference; }
bool is_void() const { return m_is_void; }
@@ -0,0 +1,58 @@
+// Tests to make sure that type conversions happen only when they should
+
+#include <chaiscript/chaiscript.hpp>
+
+void f1(int)
+{
+}
+
+void f4(std::string)
+{
+}
+
+void f2(int)
+{
+}
+
+void f3(double)
+{
+}
+
+
+int main()
+{
+ chaiscript::ChaiScript chai;
+
+ chai.add(chaiscript::fun(&f1), "f1");
+ chai.add(chaiscript::fun(&f2), "f2");
+ chai.add(chaiscript::fun(&f3), "f2");
+ chai.add(chaiscript::fun(&f1), "f3");
+ chai.add(chaiscript::fun(&f4), "f3");
+
+ // no overloads
+ chai.eval("f1(0)");
+ chai.eval("f1(0l)");
+ chai.eval("f1(0ul)");
+ chai.eval("f1(0ll)");
+ chai.eval("f1(0ull)");
+ chai.eval("f1(0.0)");
+ chai.eval("f1(0.0f)");
+ chai.eval("f1(0.0l)");
+
+ // expected overloads
+ chai.eval("f2(1)");
+ chai.eval("f2(1.0)");
+
+ // 1 non-arithmetic overload
+ chai.eval("f2(1.0)");
+
+ // this is the one call we expect to fail
+ try {
+ chai.eval("f2(1.0l)");
+ } catch (const std::exception &) {
+ return EXIT_SUCCESS;
+ }
+
+ // if the last one did not throw, we failed
+ return EXIT_FAILURE;
+}

0 comments on commit 0ea8931

Please sign in to comment.