static TTuple to_c_tuple( PyObject* py_obj ){ if( !convertible( py_obj ) ){ throw std::runtime_error( "Unable to construct boost::tuples::tuple from Python object!" ); } TTuple c_tuple; python::object py_sequence( handle<>( borrowed( py_obj ) ) ); construct_impl( py_sequence, c_tuple, mpl::int_< 0 >(), length_type() ); return c_tuple; }
// Select the best matching function for 'call' from 'candidateList'. // // Assumptions // // There is no exact match, so a selection algorithm needs to run. That is, the // language-specific handler should check for exact match first, to // decide what to do, before calling this selector. // // Input // // * list of candidate signatures to select from // * the call // * a predicate function convertible(from, to) that says whether or not type // 'from' can implicitly convert to type 'to' (it includes the case of what // the calling language would consider a matching type with no conversion // needed) // * a predicate function better(from1, from2, to1, to2) that says whether or // not a conversion from <-> to2 is considered better than a conversion // from <-> to1 (both in and out directions need testing, as declared by the // formal parameter) // // Output // // * best matching candidate (or none, if no viable candidates found) // * whether there was a tie for the best match (ambiguous overload selection, // caller's choice for how to report) // const TFunction* TParseContextBase::selectFunction( const TVector<const TFunction*> candidateList, const TFunction& call, std::function<bool(const TType& from, const TType& to, TOperator op, int arg)> convertible, std::function<bool(const TType& from, const TType& to1, const TType& to2)> better, /* output */ bool& tie) { // // Operation // // 1. Prune the input list of candidates down to a list of viable candidates, // where each viable candidate has // // * at least as many parameters as there are calling arguments, with any // remaining parameters being optional or having default values // * each parameter is true under convertible(A, B), where A is the calling // type for in and B is the formal type, and in addition, for out B is the // calling type and A is the formal type // // 2. If there are no viable candidates, return with no match. // // 3. If there is only one viable candidate, it is the best match. // // 4. If there are multiple viable candidates, select the first viable candidate // as the incumbent. Compare the incumbent to the next viable candidate, and if // that candidate is better (bullets below), make it the incumbent. Repeat, with // a linear walk through the viable candidate list. The final incumbent will be // returned as the best match. A viable candidate is better than the incumbent if // // * it has a function argument with a better(...) conversion than the incumbent, // for all directions needed by in and out // * the incumbent has no argument with a better(...) conversion then the // candidate, for either in or out (as needed) // // 5. Check for ambiguity by comparing the best match against all other viable // candidates. If any other viable candidate has a function argument with a // better(...) conversion than the best candidate (for either in or out // directions), return that there was a tie for best. // tie = false; // 1. prune to viable... TVector<const TFunction*> viableCandidates; for (auto it = candidateList.begin(); it != candidateList.end(); ++it) { const TFunction& candidate = *(*it); // to even be a potential match, number of arguments must be >= the number of // fixed (non-default) parameters, and <= the total (including parameter with defaults). if (call.getParamCount() < candidate.getFixedParamCount() || call.getParamCount() > candidate.getParamCount()) continue; // see if arguments are convertible bool viable = true; // The call can have fewer parameters than the candidate, if some have defaults. const int paramCount = std::min(call.getParamCount(), candidate.getParamCount()); for (int param = 0; param < paramCount; ++param) { if (candidate[param].type->getQualifier().isParamInput()) { if (! convertible(*call[param].type, *candidate[param].type, candidate.getBuiltInOp(), param)) { viable = false; break; } } if (candidate[param].type->getQualifier().isParamOutput()) { if (! convertible(*candidate[param].type, *call[param].type, candidate.getBuiltInOp(), param)) { viable = false; break; } } } if (viable) viableCandidates.push_back(&candidate); } // 2. none viable... if (viableCandidates.size() == 0) return nullptr; // 3. only one viable... if (viableCandidates.size() == 1) return viableCandidates.front(); // 4. find best... const auto betterParam = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool { // is call -> can2 better than call -> can1 for any parameter bool hasBetterParam = false; for (int param = 0; param < call.getParamCount(); ++param) { if (better(*call[param].type, *can1[param].type, *can2[param].type)) { hasBetterParam = true; break; } } return hasBetterParam; }; const auto equivalentParams = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool { // is call -> can2 equivalent to call -> can1 for all the call parameters? for (int param = 0; param < call.getParamCount(); ++param) { if (better(*call[param].type, *can1[param].type, *can2[param].type) || better(*call[param].type, *can2[param].type, *can1[param].type)) return false; } return true; }; const TFunction* incumbent = viableCandidates.front(); for (auto it = viableCandidates.begin() + 1; it != viableCandidates.end(); ++it) { const TFunction& candidate = *(*it); if (betterParam(*incumbent, candidate) && ! betterParam(candidate, *incumbent)) incumbent = &candidate; } // 5. ambiguity... for (auto it = viableCandidates.begin(); it != viableCandidates.end(); ++it) { if (incumbent == *it) continue; const TFunction& candidate = *(*it); // In the case of default parameters, it may have an identical initial set, which is // also ambiguous if (betterParam(*incumbent, candidate) || equivalentParams(*incumbent, candidate)) tie = true; } return incumbent; }