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 "register_function.hpp"
|
||||||
#include "operators.hpp"
|
#include "operators.hpp"
|
||||||
#include "boxed_pod_value.hpp"
|
#include "boxed_pod_value.hpp"
|
||||||
|
#include <boost/function_types/result_type.hpp>
|
||||||
|
|
||||||
namespace chaiscript
|
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:
|
public:
|
||||||
/**
|
/**
|
||||||
* perform all common bootstrap functions for std::string, void and POD types
|
* 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<Proxy_Function>(), "function");
|
||||||
m->add(user_type<std::exception>(), "exception");
|
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(user_type<std::runtime_error>(), "runtime_error");
|
||||||
m->add(constructor<std::runtime_error (const std::string &)>(), "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");
|
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::name), "cpp_name");
|
||||||
m->add(fun(&Type_Info::bare_name), "cpp_bare_name");
|
m->add(fun(&Type_Info::bare_name), "cpp_bare_name");
|
||||||
m->add(fun(&Type_Info::bare_equal), "bare_equal");
|
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);
|
basic_constructors<bool>("bool", m);
|
||||||
|
@@ -449,26 +449,27 @@ namespace detail {
|
|||||||
assignable_type<VectorType>(type, m);
|
assignable_type<VectorType>(type, m);
|
||||||
input_range_type<VectorType>(type, m);
|
input_range_type<VectorType>(type, m);
|
||||||
|
|
||||||
|
if (typeid(VectorType) == typeid(std::vector<Boxed_Value>))
|
||||||
m->eval("def Vector::`==`(rhs) : type_match(rhs, this) { \
|
{
|
||||||
if ( rhs.size() != this.size() ) { \
|
m->eval("def Vector::`==`(rhs) : type_match(rhs, this) { \
|
||||||
return false; \
|
if ( rhs.size() != this.size() ) { \
|
||||||
} else { \
|
return false; \
|
||||||
var r1 = range(this); \
|
} else { \
|
||||||
var r2 = range(rhs); \
|
var r1 = range(this); \
|
||||||
while (!r1.empty()) \
|
var r2 = range(rhs); \
|
||||||
{ \
|
while (!r1.empty()) \
|
||||||
if (!eq(r1.front(), r2.front())) \
|
|
||||||
{ \
|
{ \
|
||||||
return false; \
|
if (!eq(r1.front(), r2.front())) \
|
||||||
|
{ \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
r1.pop_front(); \
|
||||||
|
r2.pop_front(); \
|
||||||
} \
|
} \
|
||||||
r1.pop_front(); \
|
return true; \
|
||||||
r2.pop_front(); \
|
} \
|
||||||
} \
|
}");
|
||||||
return true; \
|
}
|
||||||
} \
|
|
||||||
}");
|
|
||||||
|
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
@@ -120,22 +120,70 @@ namespace chaiscript
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Dispatch_Function(const std::vector<std::pair<std::string, Proxy_Function > > &t_funcs)
|
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)
|
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 ~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
|
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
|
virtual bool call_match(const std::vector<Boxed_Value> &vals) const
|
||||||
@@ -160,7 +208,7 @@ namespace chaiscript
|
|||||||
|
|
||||||
virtual std::string annotation() const
|
virtual std::string annotation() const
|
||||||
{
|
{
|
||||||
return "";
|
return "Multiple method dispatch function wrapper.";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -171,6 +219,54 @@ namespace chaiscript
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::pair<std::string, Proxy_Function > > m_funcs;
|
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);
|
throw std::range_error("Object not known: " + name);
|
||||||
} else {
|
} 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)));
|
Boxed_Value f(Const_Proxy_Function(new Dispatch_Function(funcs)));
|
||||||
return f;
|
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
|
virtual int get_arity() const
|
||||||
{
|
{
|
||||||
|
@@ -77,6 +77,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;
|
||||||
|
|
||||||
|
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
|
//! Return true if the function is a possible match
|
||||||
//! to the passed in values
|
//! 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
|
virtual bool call_match(const std::vector<Boxed_Value> &vals) const
|
||||||
@@ -255,6 +260,7 @@ namespace chaiscript
|
|||||||
{
|
{
|
||||||
std::vector<Type_Info> types;
|
std::vector<Type_Info> types;
|
||||||
|
|
||||||
|
// For the return type
|
||||||
types.push_back(detail::Get_Type_Info<Boxed_Value>::get());
|
types.push_back(detail::Get_Type_Info<Boxed_Value>::get());
|
||||||
|
|
||||||
if (arity >= 0)
|
if (arity >= 0)
|
||||||
@@ -263,8 +269,6 @@ namespace chaiscript
|
|||||||
{
|
{
|
||||||
types.push_back(detail::Get_Type_Info<Boxed_Value>::get());
|
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;
|
return types;
|
||||||
@@ -318,6 +322,14 @@ namespace chaiscript
|
|||||||
return (*m_f)(build_param_list(params));
|
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
|
std::vector<Boxed_Value> build_param_list(const std::vector<Boxed_Value> ¶ms) const
|
||||||
{
|
{
|
||||||
typedef std::vector<Boxed_Value>::const_iterator pitr;
|
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)
|
def assert_throws(desc, x)
|
||||||
{
|
{
|
||||||
if (throws_exception(x))
|
if (throws_exception(x))
|
||||||
|
Reference in New Issue
Block a user