Add function meta data functions, plus related tests, and some various cleanups for how functions are constructed internally
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#include "register_function.hpp"
|
||||
#include "operators.hpp"
|
||||
#include "boxed_pod_value.hpp"
|
||||
#include <boost/function_types/result_type.hpp>
|
||||
|
||||
namespace chaiscript
|
||||
{
|
||||
@@ -505,6 +506,32 @@ namespace chaiscript
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FunctionType>
|
||||
static std::vector<Boxed_Value> do_return_boxed_value_vector(const boost::function<FunctionType> &f,
|
||||
const Proxy_Function_Base *b)
|
||||
{
|
||||
typedef typename boost::function_types::result_type<FunctionType>::type Vector;
|
||||
Vector v = f(b);
|
||||
|
||||
std::vector<Boxed_Value> vbv;
|
||||
for (typename Vector::const_iterator itr = v.begin();
|
||||
itr != v.end();
|
||||
++itr)
|
||||
{
|
||||
vbv.push_back(const_var(*itr));
|
||||
}
|
||||
|
||||
return vbv;
|
||||
}
|
||||
|
||||
template<typename Function>
|
||||
static boost::function<std::vector<Boxed_Value> (const Proxy_Function_Base*)> return_boxed_value_vector(const Function &f)
|
||||
{
|
||||
typedef typename boost::function_types::result_type<Function>::type Vector;
|
||||
boost::function<Vector (const Proxy_Function_Base *)> func(f);
|
||||
return boost::bind(&do_return_boxed_value_vector<Vector (const Proxy_Function_Base *)>, func, _1);
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* perform all common bootstrap functions for std::string, void and POD types
|
||||
@@ -518,6 +545,16 @@ namespace chaiscript
|
||||
m->add(user_type<Proxy_Function>(), "function");
|
||||
m->add(user_type<std::exception>(), "exception");
|
||||
|
||||
m->add(fun(&Proxy_Function_Base::get_arity), "get_arity");
|
||||
m->add(fun(&Proxy_Function_Base::annotation), "get_annotation");
|
||||
m->add(fun(&Proxy_Function_Base::operator()), "call");
|
||||
m->add(fun(&Proxy_Function_Base::operator==), "==");
|
||||
|
||||
|
||||
m->add(fun(return_boxed_value_vector(&Proxy_Function_Base::get_param_types)), "get_param_types");
|
||||
m->add(fun(return_boxed_value_vector(&Proxy_Function_Base::get_contained_functions)), "get_contained_functions");
|
||||
|
||||
|
||||
m->add(user_type<std::runtime_error>(), "runtime_error");
|
||||
m->add(constructor<std::runtime_error (const std::string &)>(), "runtime_error");
|
||||
m->add(fun(boost::function<std::string (const std::runtime_error &)>(&what)), "what");
|
||||
@@ -550,6 +587,9 @@ namespace chaiscript
|
||||
m->add(fun(&Type_Info::name), "cpp_name");
|
||||
m->add(fun(&Type_Info::bare_name), "cpp_bare_name");
|
||||
m->add(fun(&Type_Info::bare_equal), "bare_equal");
|
||||
typedef bool (Type_Info::*typeinfocompare)(const Type_Info &) const;
|
||||
m->add(fun(typeinfocompare(&Type_Info::operator==)), "==");
|
||||
m->add(fun(&Type_Info::bare_equal), "bare_equal");
|
||||
|
||||
|
||||
basic_constructors<bool>("bool", m);
|
||||
|
@@ -449,26 +449,27 @@ namespace detail {
|
||||
assignable_type<VectorType>(type, m);
|
||||
input_range_type<VectorType>(type, m);
|
||||
|
||||
|
||||
m->eval("def Vector::`==`(rhs) : type_match(rhs, this) { \
|
||||
if ( rhs.size() != this.size() ) { \
|
||||
return false; \
|
||||
} else { \
|
||||
var r1 = range(this); \
|
||||
var r2 = range(rhs); \
|
||||
while (!r1.empty()) \
|
||||
{ \
|
||||
if (!eq(r1.front(), r2.front())) \
|
||||
if (typeid(VectorType) == typeid(std::vector<Boxed_Value>))
|
||||
{
|
||||
m->eval("def Vector::`==`(rhs) : type_match(rhs, this) { \
|
||||
if ( rhs.size() != this.size() ) { \
|
||||
return false; \
|
||||
} else { \
|
||||
var r1 = range(this); \
|
||||
var r2 = range(rhs); \
|
||||
while (!r1.empty()) \
|
||||
{ \
|
||||
return false; \
|
||||
if (!eq(r1.front(), r2.front())) \
|
||||
{ \
|
||||
return false; \
|
||||
} \
|
||||
r1.pop_front(); \
|
||||
r2.pop_front(); \
|
||||
} \
|
||||
r1.pop_front(); \
|
||||
r2.pop_front(); \
|
||||
} \
|
||||
return true; \
|
||||
} \
|
||||
}");
|
||||
|
||||
return true; \
|
||||
} \
|
||||
}");
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
@@ -120,22 +120,70 @@ namespace chaiscript
|
||||
{
|
||||
public:
|
||||
Dispatch_Function(const std::vector<std::pair<std::string, Proxy_Function > > &t_funcs)
|
||||
: Proxy_Function_Base(std::vector<Type_Info>()),
|
||||
: Proxy_Function_Base(build_type_infos(t_funcs)),
|
||||
m_funcs(t_funcs)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool operator==(const Proxy_Function_Base &) const
|
||||
virtual bool operator==(const Proxy_Function_Base &rhs) const
|
||||
{
|
||||
return false;
|
||||
try {
|
||||
const Dispatch_Function &dispatchfun = dynamic_cast<const Dispatch_Function &>(rhs);
|
||||
return m_funcs == dispatchfun.m_funcs;
|
||||
} catch (const std::bad_cast &) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~Dispatch_Function() {}
|
||||
|
||||
virtual std::vector<Const_Proxy_Function> get_contained_functions() const
|
||||
{
|
||||
typedef std::vector<std::pair<std::string, Proxy_Function > > function_vec;
|
||||
|
||||
function_vec::const_iterator begin = m_funcs.begin();
|
||||
const function_vec::const_iterator end = m_funcs.end();
|
||||
|
||||
std::vector<Const_Proxy_Function> fs;
|
||||
|
||||
while (begin != end)
|
||||
{
|
||||
fs.push_back(begin->second);
|
||||
++begin;
|
||||
}
|
||||
|
||||
return fs;
|
||||
}
|
||||
|
||||
|
||||
virtual int get_arity() const
|
||||
{
|
||||
return -1;
|
||||
typedef std::vector<std::pair<std::string, Proxy_Function > > function_vec;
|
||||
|
||||
function_vec::const_iterator begin = m_funcs.begin();
|
||||
const function_vec::const_iterator end = m_funcs.end();
|
||||
|
||||
if (begin != end)
|
||||
{
|
||||
int arity = begin->second->get_arity();
|
||||
|
||||
++begin;
|
||||
|
||||
while (begin != end)
|
||||
{
|
||||
if (arity != begin->second->get_arity())
|
||||
{
|
||||
// The arities in the list do not match, so it's unspecified
|
||||
return -1;
|
||||
}
|
||||
|
||||
++begin;
|
||||
}
|
||||
|
||||
return arity;
|
||||
}
|
||||
|
||||
return -1; // unknown arity
|
||||
}
|
||||
|
||||
virtual bool call_match(const std::vector<Boxed_Value> &vals) const
|
||||
@@ -160,7 +208,7 @@ namespace chaiscript
|
||||
|
||||
virtual std::string annotation() const
|
||||
{
|
||||
return "";
|
||||
return "Multiple method dispatch function wrapper.";
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -171,6 +219,54 @@ namespace chaiscript
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::string, Proxy_Function > > m_funcs;
|
||||
|
||||
static std::vector<Type_Info> build_type_infos(const std::vector<std::pair<std::string, Proxy_Function > > &t_funcs)
|
||||
{
|
||||
typedef std::vector<std::pair<std::string, Proxy_Function > > function_vec;
|
||||
|
||||
function_vec::const_iterator begin = t_funcs.begin();
|
||||
const function_vec::const_iterator end = t_funcs.end();
|
||||
|
||||
if (begin != end)
|
||||
{
|
||||
std::vector<Type_Info> type_infos = begin->second->get_param_types();
|
||||
|
||||
++begin;
|
||||
|
||||
bool sizemismatch = false;
|
||||
|
||||
while (begin != end)
|
||||
{
|
||||
std::vector<Type_Info> param_types = begin->second->get_param_types();
|
||||
|
||||
if (param_types.size() != type_infos.size())
|
||||
{
|
||||
sizemismatch = true;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < type_infos.size() && i < param_types.size(); ++i)
|
||||
{
|
||||
if (!(type_infos[i] == param_types[i]))
|
||||
{
|
||||
type_infos[i] = detail::Get_Type_Info<Boxed_Value>::get();
|
||||
}
|
||||
}
|
||||
|
||||
++begin;
|
||||
}
|
||||
|
||||
assert(type_infos.size() > 0 && " type_info vector size is < 0, this is only possible if something else is broken");
|
||||
|
||||
if (sizemismatch)
|
||||
{
|
||||
type_infos.resize(1);
|
||||
}
|
||||
|
||||
return type_infos;
|
||||
}
|
||||
|
||||
return std::vector<Type_Info>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -412,6 +508,15 @@ namespace chaiscript
|
||||
{
|
||||
throw std::range_error("Object not known: " + name);
|
||||
} else {
|
||||
|
||||
if (funcs.size() == 1)
|
||||
{
|
||||
// Return the first item if there is only one,
|
||||
// no reason to take the cast of the extra level of dispatch
|
||||
return const_var(funcs.begin()->second);
|
||||
}
|
||||
|
||||
|
||||
Boxed_Value f(Const_Proxy_Function(new Dispatch_Function(funcs)));
|
||||
return f;
|
||||
}
|
||||
|
@@ -90,6 +90,13 @@ namespace chaiscript
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::vector<Const_Proxy_Function> get_contained_functions() const
|
||||
{
|
||||
std::vector<Const_Proxy_Function> fs;
|
||||
fs.push_back(m_func);
|
||||
return fs;
|
||||
}
|
||||
|
||||
|
||||
virtual int get_arity() const
|
||||
{
|
||||
|
@@ -77,6 +77,11 @@ namespace chaiscript
|
||||
virtual bool operator==(const Proxy_Function_Base &) const = 0;
|
||||
virtual bool call_match(const std::vector<Boxed_Value> &vals) const = 0;
|
||||
|
||||
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 true if the function is a possible match
|
||||
//! to the passed in values
|
||||
@@ -192,9 +197,9 @@ namespace chaiscript
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool operator==(const Proxy_Function_Base &) const
|
||||
virtual bool operator==(const Proxy_Function_Base &rhs) const
|
||||
{
|
||||
return false;
|
||||
return this == &rhs;
|
||||
}
|
||||
|
||||
virtual bool call_match(const std::vector<Boxed_Value> &vals) const
|
||||
@@ -255,6 +260,7 @@ namespace chaiscript
|
||||
{
|
||||
std::vector<Type_Info> types;
|
||||
|
||||
// For the return type
|
||||
types.push_back(detail::Get_Type_Info<Boxed_Value>::get());
|
||||
|
||||
if (arity >= 0)
|
||||
@@ -263,8 +269,6 @@ namespace chaiscript
|
||||
{
|
||||
types.push_back(detail::Get_Type_Info<Boxed_Value>::get());
|
||||
}
|
||||
} else {
|
||||
types.push_back(detail::Get_Type_Info<std::vector<Boxed_Value> >::get());
|
||||
}
|
||||
|
||||
return types;
|
||||
@@ -318,6 +322,14 @@ namespace chaiscript
|
||||
return (*m_f)(build_param_list(params));
|
||||
}
|
||||
|
||||
virtual std::vector<Const_Proxy_Function> get_contained_functions() const
|
||||
{
|
||||
std::vector<Const_Proxy_Function> fs;
|
||||
fs.push_back(m_f);
|
||||
return fs;
|
||||
}
|
||||
|
||||
|
||||
std::vector<Boxed_Value> build_param_list(const std::vector<Boxed_Value> ¶ms) const
|
||||
{
|
||||
typedef std::vector<Boxed_Value>::const_iterator pitr;
|
||||
|
11
unittests/dispatch_functions.chai
Normal file
11
unittests/dispatch_functions.chai
Normal file
@@ -0,0 +1,11 @@
|
||||
assert_equal(`==`, `==`);
|
||||
assert_not_equal(`==`, `<`);
|
||||
assert_equal(`<`.get_arity(), 2);
|
||||
assert_equal(`+`.get_annotation(), "Multiple method dispatch function wrapper.");
|
||||
assert_equal(get_arity.get_contained_functions().size(), 0);
|
||||
assert_equal(get_arity.get_arity(), 1);
|
||||
assert_equal(get_arity.get_param_types().size(), 2);
|
||||
|
||||
var paramtypes = get_arity.get_param_types();
|
||||
|
||||
assert_equal(true, paramtypes[1].bare_equal(function_type));
|
28
unittests/function_introspection.chai
Normal file
28
unittests/function_introspection.chai
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
#Test Function Description
|
||||
def test_function(a)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
assert_equal(test_function.get_arity(), 1);
|
||||
assert_equal(test_function.get_annotation(), "#Test Function Description\n");
|
||||
assert_equal(test_function.get_contained_functions().size(), 0);
|
||||
assert_equal(test_function.get_param_types().size(), 2);
|
||||
|
||||
assert_equal(test_function, test_function);
|
||||
|
||||
assert_not_equal(test_function, `+`);
|
||||
|
||||
assert_equal(test_function.call([1]), 1);
|
||||
|
||||
assert_equal(2, `==`.get_arity());
|
||||
|
||||
// < should be the merging of two functions bool <(PODObject, PODObject) and bool <(string, string)
|
||||
// we want to peel it apart and make sure that's true
|
||||
var types = `<`.get_param_types();
|
||||
assert_equal(3, types.size());
|
||||
assert_equal(true, types[0].bare_equal(bool_type));
|
||||
assert_equal(true, types[1].bare_equal(Object_type));
|
||||
assert_equal(true, types[2].bare_equal(Object_type));
|
||||
assert_equal(2, `<`.get_contained_functions().size());
|
@@ -10,6 +10,18 @@ def assert_equal(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
def assert_not_equal(x, y)
|
||||
{
|
||||
if (!(x == y))
|
||||
{
|
||||
// Passes
|
||||
} else {
|
||||
// Fails
|
||||
print("assert_not_equal failure: got " + to_string(y) + " which was not expected.");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
def assert_throws(desc, x)
|
||||
{
|
||||
if (throws_exception(x))
|
||||
|
Reference in New Issue
Block a user