Partial template specialization fixes to support default arguments

Default argments come from the primary template's parameter list.

Example:
  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>;
This commit is contained in:
William S Fulton 2023-03-01 18:34:09 +00:00
parent a55e429bf2
commit 9924c5c3e1
9 changed files with 220 additions and 33 deletions

View File

@ -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) 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 2023-02-15: wsfulton
#1300 Further partial template specialization fixes. #1300 Further partial template specialization fixes.
Fixes when templates are used as a template parameter in a partially specialized Fixes when templates are used as a template parameter in a partially specialized

View File

@ -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>;

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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 // (5) Default args used in specialization, like std::list
%include <std_string.i>
%inline %{ %inline %{
template <typename A> struct Allocator {}; template <typename A> struct Allocator {};
template <typename T, class Alloc = Allocator<T> > struct Lyst { void primary(T, Allocator<T>) {} }; 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 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) {} }; 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) {} };
//template <typename TTTT> struct Lyst<const TTTT&> { void specialized3(TTTT) {} };
void test_list() { void test_list() {
int myint = 0; double mydouble = 0;
Lyst<int> lis; Lyst<double>().primary(mydouble, Allocator<double>());
lis.primary(myint, Allocator<int>()); Lyst<short, Allocator<short> >().primary(mydouble, Allocator<short>());
PlainStruct ps; PlainStruct ps;
Lyst<PlainStruct *> liss; int myint = 0;
liss.specialized1(ps, Allocator<PlainStruct *>()); std::string mystring = 0;
Lyst<PlainStruct *>().specialized1(ps, Allocator<PlainStruct *>());
double mydouble = 0; Lyst<double **>().specialized2(mydouble, (double **)0);
Lyst<double **> lissd; Lyst<const int&>().specialized3(myint);
lissd.specialized2(mydouble, (double **)0); // Specifying the default still calls the partially specialized template
Lyst<std::string const &, Allocator<std::string const &> >().specialized3(mystring);
// Lyst<const int&> lissconstint;
// lissconstint.specialized3(myint);
} }
%} %}
%template(AllocatorDouble) Allocator<double>;
%template(AllocatorShort) Allocator<short>;
%template(AllocatorPlainStructPtr) Allocator<PlainStruct *>;
%template(LystDouble) Lyst<double>; %template(LystDouble) Lyst<double>;
//%template(LystDouble) Lyst<short, Allocator<short> >; %template(LystShort) Lyst<short, Allocator<short> >;
%template(LystPlainStructPtr) Lyst<PlainStruct *>; %template(LystPlainStructPtr) Lyst<PlainStruct *>;
%template(LystDoublePtrPtr) Lyst<double **>; // called specialized1 instead of specialized2 %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);
}
%}

View File

@ -69,6 +69,7 @@ extern "C" {
extern Node *Swig_cparse_template_locate(String *name, ParmList *tparms, String *symname, Symtab *tscope); extern Node *Swig_cparse_template_locate(String *name, ParmList *tparms, String *symname, Symtab *tscope);
extern void Swig_cparse_debug_templates(int); 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_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 #ifdef __cplusplus
} }

View File

@ -4121,10 +4121,10 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN {
set_nodeType($$,"template"); set_nodeType($$,"template");
/* Template partial specialization */ /* Template partial specialization */
if (tempn && ($3) && ($6)) { if (tempn && ($3) && ($6)) {
List *tlist; ParmList *primary_templateparms = Getattr(tempn, "templateparms");
String *targs = SwigType_templateargs(tname); String *targs = SwigType_templateargs(tname); /* tname contains name and specialized template parameters, for example: X<(p.T,TT)> */
tlist = SwigType_parmlist(targs); List *tlist = SwigType_parmlist(targs);
/* Printf(stdout,"targs = '%s' %s\n", targs, tlist); */ int specialization_parms_len = Len(tlist);
if (!Getattr($$,"sym:weak")) { if (!Getattr($$,"sym:weak")) {
Setattr($$,"sym:typename","1"); Setattr($$,"sym:typename","1");
} }
@ -4133,13 +4133,15 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN {
Delattr($$, "specialization"); Delattr($$, "specialization");
Setattr($$, "partialspecialization", "1"); Setattr($$, "partialspecialization", "1");
if (Len(tlist) != ParmList_len(Getattr(tempn,"templateparms"))) { if (specialization_parms_len > ParmList_len(primary_templateparms)) {
Swig_error(Getfile($$),Getline($$),"Inconsistent argument count in template partial specialization. %d %d\n", Len(tlist), ParmList_len(Getattr(tempn,"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 { } else {
/* Create a specialized name with template parameters replaced with $ variables, such as, X<(T1,p.T2) => X<($1,p.$2)> */ /* Create a specialized name with template parameters replaced with $ variables, such as, X<(T1,p.T2) => X<($1,p.$2)> */
Parm *p = $3; Parm *p = $3;
String *fname = NewString(Getattr($$,"name")); String *fname = NewString(tname);
String *ffname = 0; String *ffname = 0;
ParmList *partialparms = 0; ParmList *partialparms = 0;
@ -4183,6 +4185,27 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN {
Delete(tparms); Delete(tparms);
Append(ffname,")>"); 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(); Node *new_partial = NewHash();
String *partials = Getattr(tempn,"partials"); String *partials = Getattr(tempn,"partials");

View File

@ -498,7 +498,6 @@ int Swig_cparse_template_expand(Node *n, String *rname, ParmList *tparms, Symtab
p = nextSibling(p); p = nextSibling(p);
tp = nextSibling(tp); tp = nextSibling(tp);
} }
assert(ParmList_len(ptargs) == ParmList_len(tparms));
Delete(ptargs); Delete(ptargs);
} else { } else {
Setattr(n, "templateparmsraw", Getattr(n, "templateparms")); 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"); targs = Getattr(templ, "templateparms");
expandedparms = Swig_symbol_template_defargs(parms, targs, tscope, primary_scope); expandedparms = Swig_symbol_template_defargs(parms, targs, tscope, primary_scope);
/* reduce the typedef */ /* Qualify template parameters */
p = expandedparms; p = expandedparms;
while (p) { while (p) {
SwigType *ty = Getattr(p, "type"); 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 */ partials = Getattr(templ, "partials"); /* note that these partial specializations do not include explicit specializations */
if (partials) { if (partials) {
Iterator pi; 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; int *priorities_row;
max_possible_partials = Len(partials); max_possible_partials = Len(partials);
priorities_matrix = (int *)Malloc(sizeof(int) * max_possible_partials * parms_len); /* slightly wasteful allocation for max possible matches */ priorities_matrix = (int *)Malloc(sizeof(int) * max_possible_partials * parms_len); /* slightly wasteful allocation for max possible matches */
priorities_row = priorities_matrix; priorities_row = priorities_matrix;
for (pi = First(partials); pi.item; pi = Next(pi)) { for (pi = First(partials); pi.item; pi = Next(pi)) {
Parm *p = parms; Parm *p = parms;
int all_parameters_match = 1;
int i = 1; int i = 1;
Parm *partialparms = Getattr(pi.item, "partialparms"); Parm *partialparms = Getattr(pi.item, "partialparms");
Parm *pp = 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); Printf(stdout, " checking match: '%s' (partial specialization)\n", templcsymname);
} }
if (ParmList_len(partialparms) == parms_len) { if (ParmList_len(partialparms) == parms_len) {
int all_parameters_match = 1;
while (p && pp) { while (p && pp) {
SwigType *t; SwigType *t;
t = Getattr(p, "type"); 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"); Parm *tparmsfound = Getattr(primary ? primary : n, "templateparms");
int specialized = !tparmsfound; /* fully specialized (an explicit specialization) */ int specialized = !tparmsfound; /* fully specialized (an explicit specialization) */
int variadic = ParmList_variadic_parm(tparmsfound) != 0; int variadic = ParmList_variadic_parm(tparmsfound) != 0;
match = n;
if (!specialized) { if (!specialized) {
if (!variadic && (ParmList_len(instantiated_parms) > ParmList_len(tparmsfound))) { 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)); 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 */ } 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"); if (match)
match = n; SetFlag(n, "instantiate");
} else { } else {
Node *firstn = 0; Node *firstn = 0;
/* If not a templated class we must have a templated function. /* If not a templated class we must have a templated function.
@ -1285,6 +1287,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() * expand_defaults()
* *
@ -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 *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")) { if (Equal(Getattr(primary, "templatetype"), "class")) {
/* Templated class */ /* Templated class */
ParmList *templateparms = Getattr(primary, "templateparms"); ParmList *templateparms = Getattr(primary, "templateparms");
expanded_templateparms = CopyParmList(instantiated_parms);
int variadic = merge_parameters(expanded_templateparms, templateparms); int variadic = merge_parameters(expanded_templateparms, templateparms);
/* Add default arguments from primary template */ /* Add default arguments from primary template */
if (!variadic) { if (!variadic) {
@ -1345,9 +1362,43 @@ ParmList *Swig_cparse_template_parms_expand(ParmList *instantiated_parms, Node *
/* Templated function */ /* Templated function */
/* TODO: Default template parameters support was only added in C++11 */ /* TODO: Default template parameters support was only added in C++11 */
ParmList *templateparms = Getattr(templ, "templateparms"); ParmList *templateparms = Getattr(templ, "templateparms");
expanded_templateparms = CopyParmList(instantiated_parms);
merge_parameters(expanded_templateparms, templateparms); merge_parameters(expanded_templateparms, templateparms);
} }
return expanded_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;
}

View File

@ -190,7 +190,8 @@ Parm *ParmList_variadic_parm(ParmList *p) {
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* ParmList_numrequired() * ParmList_numrequired()
* *
* Return number of required arguments * Return number of required arguments - the number of arguments excluding
* default arguments
* ----------------------------------------------------------------------------- */ * ----------------------------------------------------------------------------- */
int ParmList_numrequired(ParmList *p) { int ParmList_numrequired(ParmList *p) {