summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.current13
-rw-r--r--Examples/test-suite/errors/cpp_template_partial_specialization_defaults.i9
-rw-r--r--Examples/test-suite/errors/cpp_template_partial_specialization_defaults.stderr2
-rw-r--r--Examples/test-suite/java/template_partial_specialization_more_runme.java49
-rw-r--r--Examples/test-suite/template_partial_specialization_more.i68
-rw-r--r--Source/CParse/cparse.h1
-rw-r--r--Source/CParse/parser.y37
-rw-r--r--Source/CParse/templ.c71
-rw-r--r--Source/Swig/parms.c3
9 files changed, 220 insertions, 33 deletions
diff --git a/CHANGES.current b/CHANGES.current
index 2c9c85330..f0b916be0 100644
--- a/CHANGES.current
+++ b/CHANGES.current
@@ -7,6 +7,19 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.2.0 (in progress)
===========================
+2023-03-01: wsfulton
+ Partial template specialization fixes to support default arguments from the primary
+ template's parameter list.
+
+ template<class Y, class T=int> struct X { void primary() {} };
+ // Previously the specialization below resulted in:
+ // Error: Inconsistent argument count in template partial specialization. 1 2
+ template<class YY> struct X<YY*> { void special(YY*) {} };
+
+ // Both of these correctly wrap the partially specialized template
+ %template(StringPtr) X<const char *>;
+ %template(ShortPtr) X<short *, int>;
+
2023-02-15: wsfulton
#1300 Further partial template specialization fixes.
Fixes when templates are used as a template parameter in a partially specialized
diff --git a/Examples/test-suite/errors/cpp_template_partial_specialization_defaults.i b/Examples/test-suite/errors/cpp_template_partial_specialization_defaults.i
new file mode 100644
index 000000000..25b2c5c36
--- /dev/null
+++ b/Examples/test-suite/errors/cpp_template_partial_specialization_defaults.i
@@ -0,0 +1,9 @@
+%module xxx
+
+template<class Y, class T=int> struct X { void primary() {} };
+template<class YY> struct X<YY*> { void special(YY*) {} };
+
+%template(Xbad1) X<>;
+%template(Xokay1) X<const char *>;
+%template(Xokay2) X<const short *, int>;
+%template(Xbad2) X<const char *, int, double>;
diff --git a/Examples/test-suite/errors/cpp_template_partial_specialization_defaults.stderr b/Examples/test-suite/errors/cpp_template_partial_specialization_defaults.stderr
new file mode 100644
index 000000000..196862431
--- /dev/null
+++ b/Examples/test-suite/errors/cpp_template_partial_specialization_defaults.stderr
@@ -0,0 +1,2 @@
+cpp_template_partial_specialization_defaults.i:6: Error: Not enough template parameters specified. Minimum of 1 required.
+cpp_template_partial_specialization_defaults.i:9: Error: Too many template parameters. Maximum of 2.
diff --git a/Examples/test-suite/java/template_partial_specialization_more_runme.java b/Examples/test-suite/java/template_partial_specialization_more_runme.java
new file mode 100644
index 000000000..c990a1bc0
--- /dev/null
+++ b/Examples/test-suite/java/template_partial_specialization_more_runme.java
@@ -0,0 +1,49 @@
+import template_partial_specialization_more.*;
+
+public class template_partial_specialization_more_runme {
+
+ static {
+ try {
+ System.loadLibrary("template_partial_specialization_more");
+ } 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[]) {
+ // (1)
+ VectInt vi = new VectInt();
+ int num = new FooVectIntDouble().partially_specialized(222);
+ new FooShortPtrDouble().pointer_specialize((short)0);
+ vi = new FooVectVectInt().partially_specialized(vi);
+
+ // (2)
+ new HeyInts().special_hey();
+
+ // (3)
+ new XX1().special1();
+ new XX2().special2();
+ new XX3().special3();
+
+ // (4)
+ new PartiallerPrimary().primary((short)0, (short)0);
+ new PartiallerSpecial().special(new PlainStruct(), 999, true);
+
+ // (5)
+ new LystDouble().primary(11.1, new AllocatorDouble());
+ new LystShort().primary((short)0, new AllocatorShort());
+ new LystPlainStructPtr().specialized1(new PlainStruct(), new AllocatorPlainStructPtr());
+ new LystDoublePtrPtr().specialized2(22.2, (SWIGTYPE_p_p_double)null);
+ new LystConstIntRef().specialized3(100);
+ new LystConstStringRef().specialized3("hello");
+
+ // (6)
+ SpecDoubleInt d = new SpecDoubleInt();
+ SpecStringInt i = new SpecStringInt();
+ d.spec_specialized(12.3);
+ i.spec_specialized("hi");
+ template_partial_specialization_more.UseSpec1(d, d);
+ template_partial_specialization_more.UseSpec2(i, i);
+ }
+}
diff --git a/Examples/test-suite/template_partial_specialization_more.i b/Examples/test-suite/template_partial_specialization_more.i
index a9e0a591b..e63ac158b 100644
--- a/Examples/test-suite/template_partial_specialization_more.i
+++ b/Examples/test-suite/template_partial_specialization_more.i
@@ -72,32 +72,70 @@ template<typename S1, typename S2> struct Partialler<S2, S1*> { void special(S1*
// (5) Default args used in specialization, like std::list
+%include <std_string.i>
%inline %{
template <typename A> struct Allocator {};
template <typename T, class Alloc = Allocator<T> > struct Lyst { void primary(T, Allocator<T>) {} };
template <typename TT, class XXAlloc> struct Lyst<TT*, XXAlloc> { void specialized1(TT, XXAlloc) {} };
template <typename TTT, class YY> struct Lyst<TTT**, Allocator<YY> > { void specialized2(TTT, YY) {} };
-// TODO Error: Inconsistent argument count in template partial specialization. 1 2
-//template <typename TTTT> struct Lyst<const TTTT&> { void specialized3(TTTT) {} };
-void test_list() {
- int myint = 0;
- Lyst<int> lis;
- lis.primary(myint, Allocator<int>());
-
- PlainStruct ps;
- Lyst<PlainStruct *> liss;
- liss.specialized1(ps, Allocator<PlainStruct *>());
+template <typename TTTT> struct Lyst<const TTTT&> { void specialized3(TTTT) {} };
+void test_list() {
double mydouble = 0;
- Lyst<double **> lissd;
- lissd.specialized2(mydouble, (double **)0);
+ Lyst<double>().primary(mydouble, Allocator<double>());
+ Lyst<short, Allocator<short> >().primary(mydouble, Allocator<short>());
-// Lyst<const int&> lissconstint;
-// lissconstint.specialized3(myint);
+ PlainStruct ps;
+ int myint = 0;
+ std::string mystring = 0;
+ Lyst<PlainStruct *>().specialized1(ps, Allocator<PlainStruct *>());
+ Lyst<double **>().specialized2(mydouble, (double **)0);
+ Lyst<const int&>().specialized3(myint);
+ // Specifying the default still calls the partially specialized template
+ Lyst<std::string const &, Allocator<std::string const &> >().specialized3(mystring);
}
%}
+%template(AllocatorDouble) Allocator<double>;
+%template(AllocatorShort) Allocator<short>;
+%template(AllocatorPlainStructPtr) Allocator<PlainStruct *>;
+
%template(LystDouble) Lyst<double>;
-//%template(LystDouble) Lyst<short, Allocator<short> >;
+%template(LystShort) Lyst<short, Allocator<short> >;
%template(LystPlainStructPtr) Lyst<PlainStruct *>;
%template(LystDoublePtrPtr) Lyst<double **>; // called specialized1 instead of specialized2
+%template(LystConstIntRef) Lyst<const int&>;
+%template(LystConstStringRef) Lyst<const std::string&, Allocator<const std::string&> >;
+
+%inline %{
+// Both parameters in each of the functions below are the same type
+void UseLystDouble(Lyst<double> a, Lyst<double, Allocator<double> > b) {}
+void UseLystShort(Lyst<short> a, Lyst<short, Allocator<short> > b) {}
+void UseLystPlainStructPtr(Lyst<PlainStruct *> a, Lyst<PlainStruct *, Allocator<PlainStruct *> > b) {}
+void UseLystDoublePtrPtr(Lyst<double **> a, Lyst<double **, Allocator<double **> > b) {}
+void UseLystConstIntRef(Lyst<const int&> a, Lyst<const int&, Allocator<const int&> > b) {}
+void UseLystConstStringRef(Lyst<const std::string&> a, Lyst<const std::string&, Allocator<const std::string&> > b) {}
+%}
+
+// (6) Default args used in specialization, more variations specifying / not specifying default
+%inline %{
+template<typename P, typename Q = int> struct Spec { void spec_primary(P p, Q q) {} };
+template<typename PP> struct Spec<const PP&, int> { void spec_specialized(PP pp) {} };
+%}
+
+%template(SpecDoubleInt) Spec<const double&, int>;
+%template(SpecStringInt) Spec<const std::string&>;
+
+%inline %{
+void UseSpec1(Spec<const double&, int> x, Spec<const double&, int> y) {}
+void UseSpec2(Spec<const std::string&, int> x, Spec<const std::string&, int> y) {}
+void test_spec() {
+ double mydouble = 0.0;
+ Spec<const double&, int>().spec_specialized(mydouble);
+ Spec<const double&>().spec_specialized(mydouble);
+
+ std::string mystring;
+ Spec<const std::string&, int>().spec_specialized(mystring);
+ Spec<const std::string&>().spec_specialized(mystring);
+}
+%}
diff --git a/Source/CParse/cparse.h b/Source/CParse/cparse.h
index db050eb91..a7a40c74d 100644
--- a/Source/CParse/cparse.h
+++ b/Source/CParse/cparse.h
@@ -69,6 +69,7 @@ extern "C" {
extern Node *Swig_cparse_template_locate(String *name, ParmList *tparms, String *symname, Symtab *tscope);
extern void Swig_cparse_debug_templates(int);
extern ParmList *Swig_cparse_template_parms_expand(ParmList *instantiated_parameters, Node *primary, Node *templ);
+ extern ParmList *Swig_cparse_template_partialargs_expand(ParmList *partially_specialized_parms, Node *primary, ParmList *templateparms);
#ifdef __cplusplus
}
diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y
index df2a87a91..10c3e8207 100644
--- a/Source/CParse/parser.y
+++ b/Source/CParse/parser.y
@@ -4121,10 +4121,10 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN {
set_nodeType($$,"template");
/* Template partial specialization */
if (tempn && ($3) && ($6)) {
- List *tlist;
- String *targs = SwigType_templateargs(tname);
- tlist = SwigType_parmlist(targs);
- /* Printf(stdout,"targs = '%s' %s\n", targs, tlist); */
+ ParmList *primary_templateparms = Getattr(tempn, "templateparms");
+ String *targs = SwigType_templateargs(tname); /* tname contains name and specialized template parameters, for example: X<(p.T,TT)> */
+ List *tlist = SwigType_parmlist(targs);
+ int specialization_parms_len = Len(tlist);
if (!Getattr($$,"sym:weak")) {
Setattr($$,"sym:typename","1");
}
@@ -4133,13 +4133,15 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN {
Delattr($$, "specialization");
Setattr($$, "partialspecialization", "1");
- if (Len(tlist) != ParmList_len(Getattr(tempn,"templateparms"))) {
- Swig_error(Getfile($$),Getline($$),"Inconsistent argument count in template partial specialization. %d %d\n", Len(tlist), ParmList_len(Getattr(tempn,"templateparms")));
+ if (specialization_parms_len > ParmList_len(primary_templateparms)) {
+ Swig_error(Getfile($$), Getline($$), "Template partial specialization has more arguments than primary template %d %d.\n", specialization_parms_len, ParmList_len(primary_templateparms));
+ } else if (specialization_parms_len < ParmList_numrequired(primary_templateparms)) {
+ Swig_error(Getfile($$), Getline($$), "Template partial specialization has fewer arguments than primary template %d %d.\n", specialization_parms_len, ParmList_len(primary_templateparms));
} else {
/* Create a specialized name with template parameters replaced with $ variables, such as, X<(T1,p.T2) => X<($1,p.$2)> */
Parm *p = $3;
- String *fname = NewString(Getattr($$,"name"));
+ String *fname = NewString(tname);
String *ffname = 0;
ParmList *partialparms = 0;
@@ -4184,6 +4186,27 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN {
Append(ffname,")>");
}
{
+ /* Replace each primary template parameter's name and value with $ variables, such as, class Y,class T=Y => class $1,class $2=$1 */
+ ParmList *primary_templateparms_copy = CopyParmList(primary_templateparms);
+ p = primary_templateparms_copy;
+ i = 0;
+ while (p) {
+ String *name = Getattr(p, "name");
+ Parm *pp = nextSibling(p);
+ ++i;
+ sprintf(tmp, "$%d", i);
+ while (pp) {
+ Replaceid(Getattr(pp, "value"), name, tmp);
+ pp = nextSibling(pp);
+ }
+ Setattr(p, "name", NewString(tmp));
+ p = nextSibling(p);
+ }
+ /* Modify partialparms by adding in missing default values ($ variables) from primary template parameters */
+ partialparms = Swig_cparse_template_partialargs_expand(partialparms, tempn, primary_templateparms_copy);
+ Delete(primary_templateparms_copy);
+ }
+ {
Node *new_partial = NewHash();
String *partials = Getattr(tempn,"partials");
if (!partials) {
diff --git a/Source/CParse/templ.c b/Source/CParse/templ.c
index 8c00570be..e9f00c429 100644
--- a/Source/CParse/templ.c
+++ b/Source/CParse/templ.c
@@ -498,7 +498,6 @@ int Swig_cparse_template_expand(Node *n, String *rname, ParmList *tparms, Symtab
p = nextSibling(p);
tp = nextSibling(tp);
}
- assert(ParmList_len(ptargs) == ParmList_len(tparms));
Delete(ptargs);
} else {
Setattr(n, "templateparmsraw", Getattr(n, "templateparms"));
@@ -835,7 +834,7 @@ static Node *template_locate(String *name, Parm *instantiated_parms, String *sym
targs = Getattr(templ, "templateparms");
expandedparms = Swig_symbol_template_defargs(parms, targs, tscope, primary_scope);
- /* reduce the typedef */
+ /* Qualify template parameters */
p = expandedparms;
while (p) {
SwigType *ty = Getattr(p, "type");
@@ -934,14 +933,13 @@ static Node *template_locate(String *name, Parm *instantiated_parms, String *sym
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 parms_len = ParmList_len(parms); /* max parameters including defaulted parameters from primary template (ie max parameters) */
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;
@@ -950,6 +948,7 @@ static Node *template_locate(String *name, Parm *instantiated_parms, String *sym
Printf(stdout, " checking match: '%s' (partial specialization)\n", templcsymname);
}
if (ParmList_len(partialparms) == parms_len) {
+ int all_parameters_match = 1;
while (p && pp) {
SwigType *t;
t = Getattr(p, "type");
@@ -1165,15 +1164,18 @@ Node *Swig_cparse_template_locate(String *name, Parm *instantiated_parms, String
Parm *tparmsfound = Getattr(primary ? primary : n, "templateparms");
int specialized = !tparmsfound; /* fully specialized (an explicit specialization) */
int variadic = ParmList_variadic_parm(tparmsfound) != 0;
+ match = n;
if (!specialized) {
if (!variadic && (ParmList_len(instantiated_parms) > ParmList_len(tparmsfound))) {
Swig_error(cparse_file, cparse_line, "Too many template parameters. Maximum of %d.\n", ParmList_len(tparmsfound));
+ match = 0;
} else if (ParmList_len(instantiated_parms) < ParmList_numrequired(tparmsfound) - (variadic ? 1 : 0)) { /* Variadic parameter is optional */
- Swig_error(cparse_file, cparse_line, "Not enough template parameters specified. %d required.\n", (ParmList_numrequired(tparmsfound) - (variadic ? 1 : 0)) );
+ Swig_error(cparse_file, cparse_line, "Not enough template parameters specified. Minimum of %d required.\n", (ParmList_numrequired(tparmsfound) - (variadic ? 1 : 0)) );
+ match = 0;
}
}
- SetFlag(n, "instantiate");
- match = n;
+ if (match)
+ SetFlag(n, "instantiate");
} else {
Node *firstn = 0;
/* If not a templated class we must have a templated function.
@@ -1286,6 +1288,22 @@ static void use_mark_defaults(ParmList *defaults) {
}
/* -----------------------------------------------------------------------------
+ * use_mark_specialized_defaults()
+ *
+ * Modify extra defaulted parameters ready for adding to specialized template parameters list
+ * ----------------------------------------------------------------------------- */
+
+static void use_mark_specialized_defaults(ParmList *defaults) {
+ Parm *tp = defaults;
+ while (tp) {
+ Setattr(tp, "default", "1");
+ Setattr(tp, "type", Getattr(tp, "value"));
+ Delattr(tp, "name");
+ tp = nextSibling(tp);
+ }
+}
+
+/* -----------------------------------------------------------------------------
* expand_defaults()
*
* Replace parameter types in default argument values, example:
@@ -1324,12 +1342,11 @@ static void expand_defaults(ParmList *expanded_templateparms) {
* ----------------------------------------------------------------------------- */
ParmList *Swig_cparse_template_parms_expand(ParmList *instantiated_parms, Node *primary, Node *templ) {
- ParmList *expanded_templateparms = 0;
+ ParmList *expanded_templateparms = CopyParmList(instantiated_parms);
if (Equal(Getattr(primary, "templatetype"), "class")) {
/* Templated class */
ParmList *templateparms = Getattr(primary, "templateparms");
- expanded_templateparms = CopyParmList(instantiated_parms);
int variadic = merge_parameters(expanded_templateparms, templateparms);
/* Add default arguments from primary template */
if (!variadic) {
@@ -1345,9 +1362,43 @@ ParmList *Swig_cparse_template_parms_expand(ParmList *instantiated_parms, Node *
/* Templated function */
/* TODO: Default template parameters support was only added in C++11 */
ParmList *templateparms = Getattr(templ, "templateparms");
- expanded_templateparms = CopyParmList(instantiated_parms);
merge_parameters(expanded_templateparms, templateparms);
}
return expanded_templateparms;
}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cparse_template_partialargs_expand()
+ *
+ * partially_specialized_parms: partially specialized template parameters
+ * primary: primary template node
+ * templateparms: primary template parameters (providing the defaults)
+ *
+ * Expand the partially_specialized_parms and return a parameter list with default
+ * arguments filled in where necessary.
+ * ----------------------------------------------------------------------------- */
+
+ParmList *Swig_cparse_template_partialargs_expand(ParmList *partially_specialized_parms, Node *primary, ParmList *templateparms) {
+ ParmList *expanded_templateparms = CopyParmList(partially_specialized_parms);
+
+ if (Equal(Getattr(primary, "templatetype"), "class")) {
+ /* Templated class */
+ int variadic = ParmList_variadic_parm(templateparms) ? 1 : 0;
+ /* Add default arguments from primary template */
+ if (!variadic) {
+ ParmList *defaults_start = ParmList_nth_parm(templateparms, ParmList_len(partially_specialized_parms));
+ if (defaults_start) {
+ ParmList *defaults = CopyParmList(defaults_start);
+ use_mark_specialized_defaults(defaults);
+ expanded_templateparms = ParmList_join(expanded_templateparms, defaults);
+ expand_defaults(expanded_templateparms);
+ }
+ }
+ } else {
+ /* Templated function */
+ /* TODO: Default template parameters support was only added in C++11 */
+ }
+
+ return expanded_templateparms;
+}
diff --git a/Source/Swig/parms.c b/Source/Swig/parms.c
index 3e052ca0c..c1dffb572 100644
--- a/Source/Swig/parms.c
+++ b/Source/Swig/parms.c
@@ -190,7 +190,8 @@ Parm *ParmList_variadic_parm(ParmList *p) {
/* -----------------------------------------------------------------------------
* ParmList_numrequired()
*
- * Return number of required arguments
+ * Return number of required arguments - the number of arguments excluding
+ * default arguments
* ----------------------------------------------------------------------------- */
int ParmList_numrequired(ParmList *p) {