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.
This commit is contained in:
parent
f24d376fa5
commit
0ea8931b21
@ -212,6 +212,10 @@ if(BUILD_TESTING)
|
|||||||
target_link_libraries(integer_literal_test ${LIBS})
|
target_link_libraries(integer_literal_test ${LIBS})
|
||||||
add_test(NAME Integer_Literal_Test COMMAND integer_literal_test)
|
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)
|
if (MULTITHREAD_SUPPORT_ENABLED)
|
||||||
add_executable(multithreaded_test unittests/multithreaded_test.cpp)
|
add_executable(multithreaded_test unittests/multithreaded_test.cpp)
|
||||||
target_link_libraries(multithreaded_test ${LIBS})
|
target_link_libraries(multithreaded_test ${LIBS})
|
||||||
|
@ -332,6 +332,46 @@ namespace chaiscript
|
|||||||
validate_boxed_number(bv);
|
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
|
template<typename Target> Target get_as() const
|
||||||
{
|
{
|
||||||
const Type_Info &inp_ = bv.get_type_info();
|
const Type_Info &inp_ = bv.get_type_info();
|
||||||
|
@ -1109,6 +1109,14 @@ namespace chaiscript
|
|||||||
vec.push_back(t_f);
|
vec.push_back(t_f);
|
||||||
std::stable_sort(vec.begin(), vec.end(), &function_less_than);
|
std::stable_sort(vec.begin(), vec.end(), &function_less_than);
|
||||||
func_objs[t_name] = Proxy_Function(new Dispatch_Function(vec));
|
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 {
|
} else {
|
||||||
std::vector<Proxy_Function> vec;
|
std::vector<Proxy_Function> vec;
|
||||||
vec.push_back(t_f);
|
vec.push_back(t_f);
|
||||||
|
@ -85,6 +85,11 @@ namespace chaiscript
|
|||||||
virtual bool operator==(const Proxy_Function_Base &) const = 0;
|
virtual bool operator==(const Proxy_Function_Base &) const = 0;
|
||||||
virtual bool call_match(const std::vector<Boxed_Value> &vals) 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
|
virtual std::vector<boost::shared_ptr<const Proxy_Function_Base> > get_contained_functions() const
|
||||||
{
|
{
|
||||||
return std::vector<boost::shared_ptr<const Proxy_Function_Base> >();
|
return std::vector<boost::shared_ptr<const Proxy_Function_Base> >();
|
||||||
@ -117,23 +122,8 @@ namespace chaiscript
|
|||||||
|
|
||||||
virtual std::string annotation() const = 0;
|
virtual std::string annotation() const = 0;
|
||||||
|
|
||||||
protected:
|
static bool compare_type_to_param(const Type_Info &ti, const Boxed_Value &bv)
|
||||||
virtual Boxed_Value do_call(const std::vector<Boxed_Value> ¶ms) 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
|
|
||||||
{
|
|
||||||
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()
|
if (ti.is_undef()
|
||||||
|| ti.bare_equal(user_type<Boxed_Value>())
|
|| ti.bare_equal(user_type<Boxed_Value>())
|
||||||
|| (!bv.get_type_info().is_undef()
|
|| (!bv.get_type_info().is_undef()
|
||||||
@ -150,6 +140,36 @@ namespace chaiscript
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
protected:
|
||||||
|
virtual Boxed_Value do_call(const std::vector<Boxed_Value> ¶ms) 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
|
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;
|
std::vector<Type_Info> m_types;
|
||||||
|
bool m_has_arithmetic_param;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,6 +627,92 @@ namespace chaiscript
|
|||||||
|
|
||||||
namespace dispatch
|
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
|
* Take a vector of functions and a vector of parameters. Attempt to execute
|
||||||
@ -634,7 +742,7 @@ namespace chaiscript
|
|||||||
++begin;
|
++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);
|
|| (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_const() const { return m_is_const; }
|
||||||
bool is_reference() const { return m_is_reference; }
|
bool is_reference() const { return m_is_reference; }
|
||||||
bool is_void() const { return m_is_void; }
|
bool is_void() const { return m_is_void; }
|
||||||
|
58
unittests/arithmetic_conversions_test.cpp
Normal file
58
unittests/arithmetic_conversions_test.cpp
Normal file
@ -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;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user