Quote:
As mentioned, the strict macros can accept classes by value if you add in some template specializations.
Okay, my gripe was that you seemed to want to disable this. If the idea is only to make it harder for new users to shoot themselves in the foot, then it's great. I agree with what most people are saying about having to know about calling conventions and that jazz being hard on new programmers, but I also want to be sure nothing changes to make it impossible for those of us who do know to take advantage of the knowledge.
Quote:
I'm not wrapping my head around this very well, could I see code for what you're doing?
I can paste code. The general algorithm is there (It's really pretty simple), but there's all sorts of support code that makes this work. If you want it, I'll share, but this can't be made to work in isolation. Either all the types used are bound through my system, or it has to be integrated right into the script engine and we'd probably have to let Witchlord do it.
int select_best_function(const sil::Script::function_set& func_set, const std::vector<std::string>& parm_list) { int best_score = -1; int best = sil::Script::ERROR_NOMATCH; bool ambiquous = false; for (sil::Script::function_set::size_type func = 0; func < func_set.size(); ++func) { if (func_set[func].parameters.size() != parm_list.size()) continue; int exact_matches = 0; bool fail = false; for (std::vector<std::string>::size_type parm = 0; parm < parm_list.size(); ++parm) { bool needs_converted = (parm_list[parm] != func_set[func].parameters[parm].name); if (needs_converted && func_set[func].parameters[parm].is_reference) { if (!func_set[func].parameters[parm].is_const) { fail = true; break; } } if (!needs_converted) exact_matches += 1; } if (fail) continue; if (exact_matches == best_score) ambiquous = true; if (exact_matches > best_score) { best = func; best_score = exact_matches; ambiquous = false; } } if (ambiquous) return sil::Script::ERROR_AMBIQUOUS; else return best; }
The algorithm is there. I do a lot of preprocessing to make this possible. Whenever a script is loaded, the functions in it are enumerated, and their declarations are parsed, so that I can store information about their parameters. This is in func_set above - it's the information for every function with the name we are trying to call.
Whenever types are bound, information must be added to tables in order to allow the any class to supply the name of the type (as Angelscript sees it. It's amazing how many people don't grasp the idea that just because a type is named 'foo' in C++ doesn't mean it can't be named 'bar' inside angelscript) and what other types it can be converted too. The function that calls this receives a function name and a list of anys. It uses this to build the func_set and the vector of parameter types.
The actual algorithm is fairly simple. We iterate over the functions in func_set, and first we check the number of parameters. Obviously, if we didn't supply the right number of parameters, it can't match.
Then we compare parameters, counting the number of exact matches. The function with the most exact matches wins. For conversions, we check to see if the conversion is possible. If not, this function can't match. (Note that the only conversion failure I checked for was when using reference types. It will allow you to convert a reference to a const reference of another type, but not to a non-const one. It could allow this, but it might then silently 'lose' output reference values. It should also check if the conversion between types is actually possible at all, but I never implemented it.)
Finally, it keeps a running tab on ambiquity. If it finds two functions with the same number of exact matches, it sets a flag. If it finds one that matches better, it clears it.
Here is the entire source file.
#include "script.h"#include "engine.h"#include "function_call_detail.h"#include <vector>namespace{ int select_best_function(const sil::Script::function_set& func_set, const std::vector<std::string>& parm_list) { int best_score = -1; int best = sil::Script::ERROR_NOMATCH; bool ambiquous = false; for (sil::Script::function_set::size_type func = 0; func < func_set.size(); ++func) { if (func_set[func].parameters.size() != parm_list.size()) continue; int exact_matches = 0; bool fail = false; for (std::vector<std::string>::size_type parm = 0; parm < parm_list.size(); ++parm) { bool needs_converted = (parm_list[parm] != func_set[func].parameters[parm].name); if (needs_converted && func_set[func].parameters[parm].is_reference) { if (!func_set[func].parameters[parm].is_const) { fail = true; break; } } if (!needs_converted) exact_matches += 1; } if (fail) continue; if (exact_matches == best_score) ambiquous = true; if (exact_matches > best_score) { best = func; best_score = exact_matches; ambiquous = false; } } if (ambiquous) return sil::Script::ERROR_AMBIQUOUS; else return best; } std::vector<std::string> make_parm_list(const sil::AnyList& parameters) { std::vector<std::string> result; for (sil::AnyList::const_iterator I = parameters.begin(); I != parameters.end(); ++I) result.push_back( sil::any_script_typename(*I) ); return result; } bool set_all_arguments( sil::FunctionCall call_instance, const sil::AnyList& parameters, const sil::Script::parameter_list& type_list) { int type_index = 0; sil::AnyList::const_iterator parameter_iter = parameters.begin(); for (; parameter_iter != parameters.end(); ++parameter_iter, ++type_index) { sil::Any converted_value = *parameter_iter; if (type_list[type_index].name != sil::any_script_typename(converted_value)) converted_value = sil::convert_any( converted_value, type_list[type_index].name ); if (!call_instance->set_argument( type_index, converted_value, type_list[type_index].is_reference )) return false; } return true; }}int sil::Script::spawn_instance( sil::FunctionCall& result, const std::string& function, const sil::AnyList& parameters){ if (!this->good_module) return sil::Script::ERROR_BADMODULE; sil::Script::function_map::iterator func_record = this->functions.find(function); if (func_record == this->functions.end()) return sil::Script::ERROR_NOMATCH; int func_id = select_best_function( func_record->second, make_parm_list(parameters) ); if (func_id < 0) return func_id; sil::FunctionCall temporary_result = new sil::c_FunctionCall( func_record->second.at(func_id).id, this->SCI ); if (!set_all_arguments( temporary_result, parameters, func_record->second.at(func_id).parameters )) { return sil::Script::ERROR_BADPARAMETER; } temporary_result->start(); this->threads.push_back(temporary_result); result = temporary_result; return 0;} sil::FunctionCall sil::Script::spawn_process( const std::string& function, const sil::AnyList& parameters){ sil::FunctionCall result; int error_code = this->spawn_instance(result,function,parameters); if (error_code != 0) return sil::FunctionCall(); return result;} int sil::Script::execute_function( sil::Any& return_value, const std::string& function, const sil::AnyList& parameters, sil::time::seconds timeout){ sil::FunctionCall func; int error_code = this->spawn_instance(func,function,parameters); if (error_code != 0) return error_code; if (!func) return sil::Script::ERROR_UNKNOWN; func->execute(timeout); if (!func->is_done()) return sil::Script::ERROR_DIDNTFINISH; if (!func->get_return_value(return_value)) return sil::Script::ERROR_CANTGETRETURN; return 0;}
I'm sorry if it doesn't make sense in isolation. I'll share the entire sil module with you if you want, but be warned that this file is fairly representative of the level of commenting in the entire thing. I actually intend to rewrite the entire thing, next time I have a project that needs scripting.