diff --git a/CHANGES.current b/CHANGES.current index 1f6de8059..16d5c6c28 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -1,6 +1,27 @@ Version 1.3.41 (in progress) ============================ +2009-10-20: wsfulton + Fixed previously fairly poor template partial specialization and explicit + specialization support. Numerous bugs in this area have been fixed including: + + - Template argument deduction implemented for template type arguments, eg this now + works: + template class X {}; + template class X {}; + %template(X1) X; // Chooses T * specialization + + and more complex cases with multiple parameters and a mix of template argument + deduction and explicitly specialised parameters, eg: + template struct TwoParm { void a() {} }; + template struct TwoParm { void e() {} }; + %template(E) TwoParm; + + Note that the primary template must now be in scope, like in C++, when + an explicit or partial specialization is instantiated with %template. + + *** POTENTIAL INCOMPATIBILITY *** + 2009-09-14: wsfulton [C#] Add %csattributes for adding C# attributes to enum values, see docs for example. diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index 4bbf74525..a47f821ef 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -339,6 +339,8 @@ CPP_TEST_CASES += \ template_ns_inherit \ template_ns_scope \ template_partial_arg \ + template_partial_specialization \ + template_partial_specialization_typedef \ template_qualifier \ template_qualifier \ template_ref_type \ diff --git a/Examples/test-suite/java/template_partial_specialization_runme.java b/Examples/test-suite/java/template_partial_specialization_runme.java new file mode 100644 index 000000000..ef8c4e80e --- /dev/null +++ b/Examples/test-suite/java/template_partial_specialization_runme.java @@ -0,0 +1,59 @@ +import template_partial_specialization.*; + +public class template_partial_specialization_runme { + + static { + try { + System.loadLibrary("template_partial_specialization"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e); + System.exit(1); + } + } + + public static void main(String argv[]) { + // One parameter tests + new A().a(); + new B().b(); + new C().c(); + new D().d(); + new E().e(); + + new F().f(); + new G().g(); + new H().h(); + + new I().i(); + new J().j(); + new K().k(); + new L().l(); + + new BB().b(); + new BBB().b(); + new BBBB().b(); + new BBBBB().b(); + + new B1().b(); + new B2().b(); + new B3().b(); + new B4().b(); + + // Two parameter tests + new A_().a(); + new B_().b(); + new C_().c(); + new D_().d(); + new E_().e(); + new F_().f(); + new G_().g(); + + new C1_().c(); + new C2_().c(); + new C3_().c(); + new C4_().c(); + new B1_().b(); + new E1_().e(); + new E2_().e(); + } +} + diff --git a/Examples/test-suite/java/template_partial_specialization_typedef_runme.java b/Examples/test-suite/java/template_partial_specialization_typedef_runme.java new file mode 100644 index 000000000..6ae95eb6a --- /dev/null +++ b/Examples/test-suite/java/template_partial_specialization_typedef_runme.java @@ -0,0 +1,59 @@ +import template_partial_specialization_typedef.*; + +public class template_partial_specialization_typedef_runme { + + static { + try { + System.loadLibrary("template_partial_specialization_typedef"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e); + System.exit(1); + } + } + + public static void main(String argv[]) { + // One parameter tests + new A().a(); + new B().b(); + new C().c(); + new D().d(); + new E().e(); + + new F().f(); + new G().g(); + new H().h(); + + new I().i(); + new J().j(); + new K().k(); + new L().l(); + + new BB().b(); + new BBB().b(); + new BBBB().b(); + new BBBBB().b(); + + new B1().b(); + new B2().b(); + new B3().b(); + new B4().b(); + + // Two parameter tests + new A_().a(); + new B_().b(); + new C_().c(); + new D_().d(); + new E_().e(); + new F_().f(); + new G_().g(); + + new C1_().c(); + new C2_().c(); + new C3_().c(); + new C4_().c(); + new B1_().b(); + new E1_().e(); + new E2_().e(); + } +} + diff --git a/Examples/test-suite/template_partial_specialization.i b/Examples/test-suite/template_partial_specialization.i new file mode 100644 index 000000000..e59ae7e5a --- /dev/null +++ b/Examples/test-suite/template_partial_specialization.i @@ -0,0 +1,134 @@ +%module template_partial_specialization + +%inline %{ +namespace One { + template struct OneParm { void a() {} }; + template struct OneParm { void b() {} }; + template struct OneParm { void c() {} }; + template struct OneParm { void d() {} }; + template struct OneParm { void e() {} }; + + template <> struct OneParm { void f() {} }; + template <> struct OneParm { void g() {} }; + template <> struct OneParm { void h() {} }; + + template <> struct OneParm { void i() {} }; + template <> struct OneParm { void j() {} }; + template <> struct OneParm { void k() {} }; + template <> struct OneParm { void l() {} }; +} +%} + +// partial specializations +%template(A) One::OneParm; +%template(B) One::OneParm; +%template(C) One::OneParm; +%template(D) One::OneParm; +%template(E) One::OneParm; + +// explicit specializations +%template(F) One::OneParm; +%template(G) One::OneParm; +%template(H) One::OneParm; + +// %template scope explicit specializations +namespace ONE { + %template(I) One::OneParm; + %template(J) ::One::OneParm; +} +%template(K) ::One::OneParm; +namespace One { + %template(L) OneParm; +} + +// %template scope partial specializations +namespace ONE { + %template(BB) One::OneParm; + %template(BBB) ::One::OneParm; +} +%template(BBBB) ::One::OneParm; +namespace One { + %template(BBBBB) OneParm; +} + +// non-exact match +%template(B1) One::OneParm; +%template(B2) One::OneParm; +%template(B3) One::OneParm; +%template(B4) One::OneParm; + + +// Two parameter specialization tests +%inline %{ +struct Concrete {}; +namespace Two { + template struct TwoParm { void a() {} }; + template struct TwoParm { void b() {} }; + template struct TwoParm { void c() {} }; + template struct TwoParm { void d() {} }; + template struct TwoParm { void e() {} }; + template struct TwoParm { void f() {} }; + template <> struct TwoParm { void g() {} }; +} +%} + +namespace Two { + %template(A_) TwoParm; + %template(B_) TwoParm; + %template(C_) TwoParm; + %template(D_) TwoParm; + %template(E_) TwoParm; + %template(F_) TwoParm; + %template(G_) TwoParm; + + %template(C1_) TwoParm; + %template(C2_) TwoParm; +} + +%template(C3_) Two::TwoParm; +%template(C4_) ::Two::TwoParm; +%template(B1_) ::Two::TwoParm; +%template(E1_) Two::TwoParm; +%template(E2_) Two::TwoParm; + +#if 0 +// TODO fix: +%inline %{ +//namespace S { + template struct X { void a() {} }; + template struct X { void b() {} }; +// template<> struct X { void c() {} }; +//} +%} + +#if 0 +struct AA { // crashes +#else +namespace AA { // thinks X is in AA namespace + %template(X2) X; +}; +#endif +#endif + +#if 0 +namespace Space { +} +template struct Vector { +#ifdef SWIG + %template() Space::VectorHelper; +#endif + void gook(T i) {} + void geeko(double d) {} + void geeky(int d) {} +}; +/* +template struct Vector { +}; +*/ +//} +%} + +%template(VectorIntPtr) Space::Vector; // should fail as Vector is in global namespace +// is this a regression - no fails in 1.3.40 too +// Note problem is removed by removing empty Space namespace!! +#endif diff --git a/Examples/test-suite/template_partial_specialization_typedef.i b/Examples/test-suite/template_partial_specialization_typedef.i new file mode 100644 index 000000000..6fdbf99aa --- /dev/null +++ b/Examples/test-suite/template_partial_specialization_typedef.i @@ -0,0 +1,130 @@ +// This testcase is almost identical to template_partial_specialization but uses typedefs for %template + +%module template_partial_specialization_typedef + +%inline %{ +namespace TypeDef { + typedef double Double; + typedef int * IntPtr; + typedef double * DoublePtr; + typedef double & DoubleRef; + typedef const double & ConstDoubleRef; + typedef double * const & DoublePtrConstRef; + + typedef int Int; + typedef int * const & IntPtrConstRef; + typedef int ** IntPtrPtr; + typedef float Float; + typedef float * FloatPtr; + typedef float ** FloatPtrPtr; + typedef float *** FloatPtrPtrPtr; + + typedef bool * BoolPtr; + typedef char * CharPtr; + typedef short * ShortPtr; + typedef long * LongPtr; + typedef unsigned int ** UnsignedIntPtrPtr; + typedef unsigned int *** UnsignedIntPtrPtrPtr; + typedef const unsigned int ** ConstUnsignedIntPtr; + typedef const unsigned int *** ConstUnsignedIntPtrPtr; +} +namespace One { + template struct OneParm { void a() {} }; + template struct OneParm { void b() {} }; + template struct OneParm { void c() {} }; + template struct OneParm { void d() {} }; + template struct OneParm { void e() {} }; + + template <> struct OneParm { void f() {} }; + template <> struct OneParm { void g() {} }; + template <> struct OneParm { void h() {} }; + + template <> struct OneParm { void i() {} }; + template <> struct OneParm { void j() {} }; + template <> struct OneParm { void k() {} }; + template <> struct OneParm { void l() {} }; +} +%} + +// partial specializations +%template(A) One::OneParm; +%template(B) One::OneParm; +%template(C) One::OneParm; +%template(D) One::OneParm; +%template(E) One::OneParm; + +// explicit specializations +%template(F) One::OneParm; +%template(G) One::OneParm; +%template(H) One::OneParm; + +// %template scope explicit specializations +namespace ONE { + %template(I) One::OneParm; + %template(J) ::One::OneParm; +} +%template(K) ::One::OneParm; +namespace One { + %template(L) OneParm; +} + +// %template scope partial specializations +namespace ONE { + %template(BB) One::OneParm; + %template(BBB) ::One::OneParm; +} +%template(BBBB) ::One::OneParm; +namespace One { + %template(BBBBB) OneParm; +} + +// non-exact match +%template(B1) One::OneParm; +%template(B2) One::OneParm; +%template(B3) One::OneParm; +%template(B4) One::OneParm; + + +// Two parameter specialization tests +%inline %{ +struct Concrete {}; +namespace Two { + template struct TwoParm { void a() {} }; + template struct TwoParm { void b() {} }; + template struct TwoParm { void c() {} }; + template struct TwoParm { void d() {} }; + template struct TwoParm { void e() {} }; + template struct TwoParm { void f() {} }; + template <> struct TwoParm { void g() {} }; +} +%} + +%inline %{ +namespace TypeDef { + typedef const double * ConstDoublePtr; + typedef const int * ConstIntPtr; + typedef int * IntPtr; + typedef Concrete * ConcretePtr; + typedef const Concrete * ConstConcretePtr; + typedef void * VoidPtr; +} +%} +namespace Two { + %template(A_) TwoParm; + %template(B_) TwoParm; + %template(C_) TwoParm; + %template(D_) TwoParm; + %template(E_) TwoParm; + %template(F_) TwoParm; + %template(G_) TwoParm; + + %template(C1_) TwoParm; + %template(C2_) TwoParm; +} + +%template(C3_) Two::TwoParm; +%template(C4_) ::Two::TwoParm; +%template(B1_) ::Two::TwoParm; +%template(E1_) Two::TwoParm; +%template(E2_) Two::TwoParm; + diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index 8876384b1..29aff3d10 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -3759,6 +3759,7 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN { template_para Parm *p = $3; String *fname = NewString(Getattr($$,"name")); String *ffname = 0; + ParmList *partialparms = 0; char tmp[32]; int i, ilen; @@ -3780,12 +3781,21 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN { template_para /* Patch argument names with typedef */ { Iterator tt; + Parm *parm_current = 0; List *tparms = SwigType_parmlist(fname); ffname = SwigType_templateprefix(fname); Append(ffname,"<("); for (tt = First(tparms); tt.item; ) { SwigType *rtt = Swig_symbol_typedef_reduce(tt.item,0); SwigType *ttr = Swig_symbol_type_qualify(rtt,0); + + Parm *newp = NewParm(ttr, 0); + if (partialparms) + set_nextSibling(partialparms, newp); + else + partialparms = newp; + parm_current = newp; + Append(ffname,ttr); tt = Next(tt); if (tt.item) Putc(',',ffname); @@ -3796,6 +3806,7 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN { template_para Append(ffname,")>"); } { + Node *new_partial = NewHash(); String *partials = Getattr(tempn,"partials"); if (!partials) { partials = NewList(); @@ -3803,7 +3814,9 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN { template_para Delete(partials); } /* Printf(stdout,"partial: fname = '%s', '%s'\n", fname, Swig_symbol_typedef_reduce(fname,0)); */ - Append(partials,ffname); + Setattr(new_partial, "partialparms", partialparms); + Setattr(new_partial, "templcsymname", ffname); + Append(partials, new_partial); } Setattr($$,"partialargs",ffname); Swig_symbol_cadd(ffname,$$); @@ -3812,8 +3825,8 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN { template_para Delete(tlist); Delete(targs); } else { - /* Need to resolve exact specialization name */ - /* add default args from generic template */ + /* An explicit template specialization */ + /* add default args from primary (unspecialized) template */ String *ty = Swig_symbol_template_deftype(tname,0); String *fname = Swig_symbol_type_qualify(ty,0); Swig_symbol_cadd(fname,$$); diff --git a/Source/CParse/templ.c b/Source/CParse/templ.c index 14886605f..1f1620094 100644 --- a/Source/CParse/templ.c +++ b/Source/CParse/templ.c @@ -422,6 +422,70 @@ int Swig_cparse_template_expand(Node *n, String *rname, ParmList *tparms, Symtab return 0; } +typedef enum { ExactNoMatch = -2, PartiallySpecializedNoMatch = -1, PartiallySpecializedMatch = 1, ExactMatch = 2 } EMatch; + +/* ----------------------------------------------------------------------------- + * does_parm_match() + * + * Template argument deduction - check if a template type matches a partially specialized + * template parameter type. Reduce 'partial_parm_type' to see if it matches 'type'. + * + * type - template parameter type to match against + * partial_parm_type - partially specialized template type - a possible match + * partial_parm_type_base - base type of partial_parm_type + * tscope - template scope + * specialization_priority - (output) contains a value indicating how good the match is + * (higher is better) only set if return is set to PartiallySpecializedMatch or ExactMatch. + * ----------------------------------------------------------------------------- */ + +static EMatch does_parm_match(SwigType *type, SwigType *partial_parm_type, const char *partial_parm_type_base, Symtab *tscope, int *specialization_priority) { + static const int EXACT_MATCH_PRIORITY = 99999; /* a number bigger than the length of any conceivable type */ + int matches; + int substitutions; + EMatch match; + SwigType *ty = Swig_symbol_typedef_reduce(type, tscope); + String *base = SwigType_base(ty); + SwigType *t = Copy(partial_parm_type); + substitutions = Replaceid(t, partial_parm_type_base, base); /* eg: Replaceid("p.$1", "$1", "int") returns t="p.int" */ + matches = Equal(ty, t); + *specialization_priority = -1; + if (substitutions == 1) { + /* we have a non-explicit specialized parameter (in partial_parm_type) because a substitution for $1, $2... etc has taken place */ + SwigType *tt = Copy(partial_parm_type); + int len; + /* + check for match to partial specialization type, for example, all of the following could match the type in the %template: + template struct XX {}; + template struct XX {}; // r.$1 + template struct XX {}; // r.q(const).$1 + template struct XX {}; // r.q(const).p.$1 + %template(XXX) XX; // r.q(const).p.int + + where type="r.q(const).p.int" will match either of tt="r.$1", tt="r.q(const)" tt="r.q(const).p" + */ + Replaceid(tt, partial_parm_type_base, ""); /* remove the $1, $2 etc, eg tt="p.$1" => "p." */ + len = Len(tt); + if (Strncmp(tt, ty, len) == 0) { + match = PartiallySpecializedMatch; + *specialization_priority = len; + } else { + match = PartiallySpecializedNoMatch; + } + Delete(tt); + } else { + match = matches ? ExactMatch : ExactNoMatch; + if (matches) + *specialization_priority = EXACT_MATCH_PRIORITY; /* exact matches always take precedence */ + } + /* + Printf(stdout, " does_parm_match %2d %5d [%s] [%s]\n", match, *specialization_priority, type, partial_parm_type); + */ + Delete(t); + Delete(base); + Delete(ty); + return match; +} + /* ----------------------------------------------------------------------------- * template_locate() * @@ -429,175 +493,321 @@ int Swig_cparse_template_expand(Node *n, String *rname, ParmList *tparms, Symtab * ----------------------------------------------------------------------------- */ static Node *template_locate(String *name, Parm *tparms, Symtab *tscope) { - Node *n; - String *tname, *rname = 0; + Node *n = 0; + String *tname = 0, *rname = 0; Node *templ; - List *mpartials = 0; + Symtab *primary_scope = 0; + List *possiblepartials = 0; Parm *p; - Parm *parms; + Parm *parms = 0; Parm *targs; ParmList *expandedparms; + int *priorities_matrix = 0; + int max_possible_partials = 0; + int posslen = 0; - tname = Copy(name); - parms = CopyParmList(tparms); - - /* Search for generic template */ + /* Search for primary (unspecialized) template */ templ = Swig_symbol_clookup(name, 0); - /* Add default values from generic template */ - if (templ) { - Symtab *tsdecl = Getattr(templ, "sym:symtab"); + if (template_debug) { + tname = Copy(name); + SwigType_add_template(tname, tparms); + Printf(stdout, "\n%s:%d: template_debug: Searching for match to: '%s'\n", cparse_file, cparse_line, tname); + Delete(tname); + tname = 0; + } + if (templ) { + tname = Copy(name); + parms = CopyParmList(tparms); + + /* All template specializations must be in the primary template's scope, store the symbol table for this scope for specialization lookups */ + primary_scope = Getattr(templ, "sym:symtab"); + + /* Add default values from primary template */ targs = Getattr(templ, "templateparms"); - expandedparms = Swig_symbol_template_defargs(parms, targs, tscope, tsdecl); - } else { - expandedparms = parms; - } + expandedparms = Swig_symbol_template_defargs(parms, targs, tscope, primary_scope); - - /* reduce the typedef */ - p = expandedparms; - while (p) { - SwigType *ty = Getattr(p, "type"); - if (ty) { - SwigType *nt = Swig_symbol_type_qualify(ty, tscope); - Setattr(p, "type", nt); - Delete(nt); - } - p = nextSibling(p); - } - - SwigType_add_template(tname, expandedparms); - - if (template_debug) { - Printf(stdout, "\n%s:%d: template_debug: Searching for %s\n", cparse_file, cparse_line, tname); - } - - /* Search for an exact specialization. - Example: template<> class name { ... } */ - { - if (template_debug) { - Printf(stdout, " searching: '%s' (exact specialization)\n", tname); - } - n = Swig_symbol_clookup_local(tname, 0); - if (!n) { - SwigType *rname = Swig_symbol_typedef_reduce(tname, tscope); - if (!Equal(rname, tname)) { - if (template_debug) { - Printf(stdout, " searching: '%s' (exact specialization)\n", rname); - } - n = Swig_symbol_clookup_local(rname, 0); + /* reduce the typedef */ + p = expandedparms; + while (p) { + SwigType *ty = Getattr(p, "type"); + if (ty) { + SwigType *nt = Swig_symbol_type_qualify(ty, tscope); + Setattr(p, "type", nt); + Delete(nt); } - Delete(rname); + p = nextSibling(p); } - if (n) { - Node *tn; - String *nodeType = nodeType(n); - if (Equal(nodeType, "template")) - goto success; - tn = Getattr(n, "template"); - if (tn) { - n = tn; - goto success; /* Previously wrapped by a template return that */ + SwigType_add_template(tname, expandedparms); + + /* Search for an explicit (exact) specialization. Example: template<> class name { ... } */ + { + if (template_debug) { + Printf(stdout, " searching for : '%s' (explicit specialization)\n", tname); } - Swig_error(cparse_file, cparse_line, "'%s' is not defined as a template. (%s)\n", name, nodeType(n)); - Delete(tname); - Delete(parms); - return 0; /* Found a match, but it's not a template of any kind. */ - } - } - - /* Search for partial specialization. - Example: template class name { ... } */ - - /* Generate reduced template name (stripped of extraneous pointers, etc.) */ - - rname = NewStringf("%s<(", name); - p = parms; - while (p) { - String *t; - t = Getattr(p, "type"); - if (!t) - t = Getattr(p, "value"); - if (t) { - String *ty = Swig_symbol_typedef_reduce(t, tscope); - String *tb = SwigType_base(ty); - String *td = SwigType_default(ty); - Replaceid(td, "enum SWIGTYPE", tb); - Replaceid(td, "SWIGTYPE", tb); - Append(rname, td); - Delete(tb); - Delete(ty); - Delete(td); - } - p = nextSibling(p); - if (p) { - Append(rname, ","); - } - } - Append(rname, ")>"); - - mpartials = NewList(); - if (templ) { - /* First, we search using an exact type prototype */ - Parm *p; - char tmp[32]; - int i; - List *partials; - String *ss; - Iterator pi; - - partials = Getattr(templ, "partials"); - if (partials) { - for (pi = First(partials); pi.item; pi = Next(pi)) { - ss = Copy(pi.item); - p = parms; - i = 1; - while (p) { - String *t, *tn; - sprintf(tmp, "$%d", i); - t = Getattr(p, "type"); - if (!t) - t = Getattr(p, "value"); - if (t) { - String *ty = Swig_symbol_typedef_reduce(t, tscope); - tn = SwigType_base(ty); - Replaceid(ss, tmp, tn); - Delete(tn); - Delete(ty); + n = Swig_symbol_clookup_local(tname, primary_scope); + if (!n) { + SwigType *rname = Swig_symbol_typedef_reduce(tname, tscope); + if (!Equal(rname, tname)) { + if (template_debug) { + Printf(stdout, " searching for : '%s' (explicit specialization with typedef reduction)\n", rname); } - i++; - p = nextSibling(p); + n = Swig_symbol_clookup_local(rname, primary_scope); } - if (template_debug) { - Printf(stdout, " searching: '%s' (partial specialization - %s)\n", ss, pi.item); - } - if ((Equal(ss, tname)) || (Equal(ss, rname))) { - Append(mpartials, pi.item); - } - Delete(ss); + Delete(rname); } - } - } - - if (template_debug) { - Printf(stdout, " Matched partials: %s\n", mpartials); - } - - if (Len(mpartials)) { - String *s = Getitem(mpartials, 0); - n = Swig_symbol_clookup_local(s, 0); - if (Len(mpartials) > 1) { if (n) { - Swig_warning(WARN_PARSE_TEMPLATE_AMBIG, cparse_file, cparse_line, "Instantiation of template '%s' is ambiguous,\n", SwigType_namestr(tname)); - Swig_warning(WARN_PARSE_TEMPLATE_AMBIG, Getfile(n), Getline(n), " instantiation '%s' is used.\n", SwigType_namestr(Getattr(n, "name"))); + Node *tn; + String *nodeType = nodeType(n); + if (Equal(nodeType, "template")) { + if (template_debug) { + Printf(stdout, " explicit specialization found: '%s'\n", Getattr(n, "name")); + } + goto success; + } + tn = Getattr(n, "template"); + if (tn) { + if (template_debug) { + Printf(stdout, " previous instantiation found: '%s'\n", Getattr(n, "name")); + } + n = tn; + goto success; /* Previously wrapped by a template instantiation */ + } + Swig_error(cparse_file, cparse_line, "'%s' is not defined as a template. (%s)\n", name, nodeType(n)); + Delete(tname); + Delete(parms); + return 0; /* Found a match, but it's not a template of any kind. */ } } + + /* Search for partial specializations. + * Example: template class name { ... } + + * There are 3 types of template arguments: + * (1) Template type arguments + * (2) Template non type arguments + * (3) Template template arguments + * only (1) is really supported for partial specializations + */ + + /* Generate reduced template name (stripped of extraneous pointers, etc.) */ + rname = NewStringf("%s<(", name); + p = parms; + while (p) { + String *t; + t = Getattr(p, "type"); + if (!t) + t = Getattr(p, "value"); + if (t) { + String *tyr = Swig_symbol_typedef_reduce(t, tscope); + String *ty = SwigType_strip_qualifiers(tyr); + String *tb = SwigType_base(ty); + String *td = SwigType_default(ty); + Replaceid(td, "enum SWIGTYPE", tb); + Replaceid(td, "SWIGTYPE", tb); + Append(rname, td); + Delete(tb); + Delete(td); + Delete(ty); + Delete(tyr); + } + p = nextSibling(p); + if (p) { + Append(rname, ","); + } + } + Append(rname, ")>"); + + /* Rank each template parameter against the desired template parameters then build a matrix of best matches */ + possiblepartials = NewList(); + { + char tmp[32]; + List *partials; + + partials = Getattr(templ, "partials"); /* note that these partial specializations do not include explicit specializations */ + if (partials) { + Iterator pi; + int parms_len = ParmList_len(parms); + int *priorities_row; + max_possible_partials = Len(partials); + priorities_matrix = (int *)malloc(sizeof(int) * max_possible_partials * parms_len); /* slightly wasteful allocation for max possible matches */ + priorities_row = priorities_matrix; + for (pi = First(partials); pi.item; pi = Next(pi)) { + Parm *p = parms; + int all_parameters_match = 1; + int i = 1; + Parm *partialparms = Getattr(pi.item, "partialparms"); + Parm *pp = partialparms; + String *templcsymname = Getattr(pi.item, "templcsymname"); + if (template_debug) { + Printf(stdout, " checking match: '%s' (partial specialization)\n", templcsymname); + } + if (ParmList_len(partialparms) == parms_len) { + while (p && pp) { + SwigType *t; + sprintf(tmp, "$%d", i); + t = Getattr(p, "type"); + if (!t) + t = Getattr(p, "value"); + if (t) { + EMatch match = does_parm_match(t, Getattr(pp, "type"), tmp, tscope, priorities_row + i - 1); + if (match < (int)PartiallySpecializedMatch) { + all_parameters_match = 0; + break; + } + } + i++; + p = nextSibling(p); + pp = nextSibling(pp); + } + if (all_parameters_match) { + Append(possiblepartials, pi.item); + priorities_row += parms_len; + } + } + } + } + } + + posslen = Len(possiblepartials); + if (template_debug) { + int i; + if (posslen == 0) + Printf(stdout, " matched partials: NONE\n"); + else if (posslen == 1) + Printf(stdout, " chosen partial: '%s'\n", Getattr(Getitem(possiblepartials, 0), "templcsymname")); + else { + Printf(stdout, " possibly matched partials:\n"); + for (i = 0; i < posslen; i++) { + Printf(stdout, " '%s'\n", Getattr(Getitem(possiblepartials, i), "templcsymname")); + } + } + } + + if (posslen > 1) { + /* Now go through all the possibly matched partial specialization templates and look for a non-ambiguous match. + * Exact matches rank the highest and deduced parameters are ranked by how much they are reduced, eg looking for + * a match to const int *, the following rank (highest to lowest): + * const int * (exact match) + * const T * + * T * + * T + * + * An ambiguous example when attempting to match as either specialization could match: %template() X; + * template X class {}; // primary template + * template X class {}; // specialization (1) + * template X class {}; // specialization (2) + */ + if (template_debug) { + int row, col; + int parms_len = ParmList_len(parms); + Printf(stdout, " parameter priorities matrix (%d parms):\n", parms_len); + for (row = 0; row < posslen; row++) { + int *priorities_row = priorities_matrix + row*parms_len; + Printf(stdout, " "); + for (col = 0; col < parms_len; col++) { + Printf(stdout, "%5d ", priorities_row[col]); + } + Printf(stdout, "\n"); + } + } + { + int row, col; + int parms_len = ParmList_len(parms); + /* Printf(stdout, " parameter priorities inverse matrix (%d parms):\n", parms_len); */ + for (col = 0; col < parms_len; col++) { + int *priorities_col = priorities_matrix + col; + int maxpriority = -1; + /* + Printf(stdout, "max_possible_partials: %d col:%d\n", max_possible_partials, col); + Printf(stdout, " "); + */ + /* determine the highest rank for this nth parameter */ + for (row = 0; row < posslen; row++) { + int *element_ptr = priorities_col + row*parms_len; + int priority = *element_ptr; + if (priority > maxpriority) + maxpriority = priority; + /* Printf(stdout, "%5d ", priority); */ + } + /* Printf(stdout, "\n"); */ + /* flag all the parameters which equal the highest rank */ + for (row = 0; row < posslen; row++) { + int *element_ptr = priorities_col + row*parms_len; + int priority = *element_ptr; + *element_ptr = (priority >= maxpriority) ? 1 : 0; + } + } + } + { + int row, col; + int parms_len = ParmList_len(parms); + Iterator pi = First(possiblepartials); + Node *chosenpartials = NewList(); + if (template_debug) + Printf(stdout, " priority flags matrix:\n"); + for (row = 0; row < posslen; row++) { + int *priorities_row = priorities_matrix + row*parms_len; + int highest_count = 0; /* count of highest priority parameters */ + for (col = 0; col < parms_len; col++) { + highest_count += priorities_row[col]; + } + if (template_debug) { + Printf(stdout, " "); + for (col = 0; col < parms_len; col++) { + Printf(stdout, "%5d ", priorities_row[col]); + } + Printf(stdout, "\n"); + } + if (highest_count == parms_len) { + Append(chosenpartials, pi.item); + } + pi = Next(pi); + } + if (Len(chosenpartials) > 0) { + /* one or more best match found */ + Delete(possiblepartials); + possiblepartials = chosenpartials; + posslen = Len(possiblepartials); + } else { + /* no best match found */ + Delete(chosenpartials); + } + } + } + + if (posslen > 0) { + String *s = Getattr(Getitem(possiblepartials, 0), "templcsymname"); + n = Swig_symbol_clookup_local(s, primary_scope); + if (posslen > 1) { + int i; + if (n) { + Swig_warning(WARN_PARSE_TEMPLATE_AMBIG, cparse_file, cparse_line, "Instantiation of template '%s' is ambiguous,\n", SwigType_namestr(tname)); + Swig_warning(WARN_PARSE_TEMPLATE_AMBIG, Getfile(n), Getline(n), " instantiation '%s' used,\n", SwigType_namestr(Getattr(n, "name"))); + } + for (i = 1; i < posslen; i++) { + String *templcsymname = Getattr(Getitem(possiblepartials, i), "templcsymname"); + Node *ignored_node = Swig_symbol_clookup_local(templcsymname, primary_scope); + Swig_warning(WARN_PARSE_TEMPLATE_AMBIG, Getfile(ignored_node), Getline(ignored_node), " instantiation '%s' ignored.\n", SwigType_namestr(Getattr(ignored_node, "name"))); + } + } + } + + if (!n) { + if (template_debug) { + Printf(stdout, " chosen primary template: '%s'\n", Getattr(templ, "name")); + } + n = templ; + } + } else { + if (template_debug) { + Printf(stdout, " primary template not found\n"); + } + /* Give up if primary (unspecialized) template not found as specializations will only exist if there is a primary template */ + n = 0; } - if (!n) { - n = templ; - } if (!n) { Swig_error(cparse_file, cparse_line, "Template '%s' undefined.\n", name); } else if (n) { @@ -610,12 +820,16 @@ static Node *template_locate(String *name, Parm *tparms, Symtab *tscope) { success: Delete(tname); Delete(rname); - Delete(mpartials); + Delete(possiblepartials); if ((template_debug) && (n)) { + /* Printf(stdout, "Node: %p\n", n); Swig_print_node(n); + */ + Printf(stdout, " chosen template:'%s'\n", Getattr(n, "name")); } Delete(parms); + free(priorities_matrix); return n; }