Instantiation of C++11 variadic function templates

Complete support for C++11 variadic function templates. Support was previously limited
to just one template parameter. Now zero or more template parameters are supported
in the %template instantiation.
This commit is contained in:
William S Fulton 2023-01-03 22:34:59 +00:00
parent cdc08c9325
commit 2fc0edc4fd
5 changed files with 132 additions and 55 deletions

View File

@ -7,6 +7,11 @@ 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-01-03: wsfulton
Complete support for C++11 variadic function templates. Support was previously limited
to just one template parameter. Now zero or more template parameters are supported
in the %template instantiation.
2022-12-29: wsfulton 2022-12-29: wsfulton
#1863 Syntax error fixes parsing more elaborate parameter pack arguments that are #1863 Syntax error fixes parsing more elaborate parameter pack arguments that are
used in function pointers, member function pointers: used in function pointers, member function pointers:
@ -50,7 +55,7 @@ Version 4.2.0 (in progress)
are function pointers. are function pointers.
2022-12-22: wsfulton 2022-12-22: wsfulton
Complete support for C++11 variadic templates. Support was previously limited Complete support for C++11 variadic class templates. Support was previously limited
to just one template parameter. Now zero or more template parameters are supported. to just one template parameter. Now zero or more template parameters are supported.
2022-12-06: wsfulton 2022-12-06: wsfulton

View File

@ -78,5 +78,19 @@ void emplace(Args &&... args) noexcept(
%} %}
%template(emplace) EmplaceContainer::emplace<int,A>; %template(emplace) EmplaceContainer::emplace<int,A>;
// TODO %template(emplace) EmplaceContainer::emplace<int,A,B>;
//%template(emplace) EmplaceContainer::emplace<int,A,B,C>; %template(emplace) EmplaceContainer::emplace<int,A,B,C>;
%template(emplace) EmplaceContainer::emplace<int,A,B,C,D>;
// Overloading mix of variadic and non-variadic templates
%inline %{
template<typename T, typename U> int variadicmix1(T t, U u) { return 10; }
template<typename... T> int variadicmix1(T... t) { return 20; }
%}
%template(variadicmix1) variadicmix1<>;
%template(variadicmix1) variadicmix1<A>;
%template(variadicmix1) variadicmix1<A,B>;
%template(variadicmix1) variadicmix1<A,B,C>;
%template(variadicmix1) variadicmix1<int, int>;

View File

@ -0,0 +1,20 @@
from cpp11_variadic_function_templates import *
ec = EmplaceContainer()
ec.emplace(A())
ec.emplace(A(), B())
ec.emplace(A(), B(), C())
ec.emplace(A(), B(), C(), D())
def check(expected, got):
if expected != got:
raise RuntimeError("failed: {} != {}".format(expected, got))
a = A()
b = B()
c = C()
check(variadicmix1(), 20)
check(variadicmix1(a), 20)
check(variadicmix1(a, b), 10)
check(variadicmix1(a, b, c), 20)
check(variadicmix1(11, 22), 10)

View File

@ -2817,8 +2817,6 @@ template_directive: SWIGTEMPLATE LPAREN idstringopt RPAREN idcolonnt LESSTHAN va
Node *n; Node *n;
Node *outer_class = currentOuterClass; Node *outer_class = currentOuterClass;
Symtab *tscope = 0; Symtab *tscope = 0;
int specialized = 0; /* fully specialized (an explicit specialization) */
int variadic = 0;
String *symname = $3 ? NewString($3) : 0; String *symname = $3 ? NewString($3) : 0;
$$ = 0; $$ = 0;
@ -2875,23 +2873,12 @@ template_directive: SWIGTEMPLATE LPAREN idstringopt RPAREN idcolonnt LESSTHAN va
Node *linkliststart = 0; Node *linkliststart = 0;
while (nn) { while (nn) {
Node *templnode = 0; Node *templnode = 0;
if (Strcmp(nodeType(nn),"template") == 0) { if (GetFlag(nn, "instantiate")) {
int nnisclass = (Strcmp(Getattr(nn,"templatetype"),"class") == 0); /* if not a templated class it is a templated function */ Delattr(nn, "instantiate");
Parm *tparms = Getattr(nn,"templateparms"); {
if (!tparms) { int nnisclass = (Strcmp(Getattr(nn, "templatetype"), "class") == 0); /* if not a templated class it is a templated function */
specialized = 1; Parm *tparms = Getattr(nn, "templateparms");
} else if (ParmList_variadic_parm(tparms)) { int specialized = !tparms; /* fully specialized (an explicit specialization) */
variadic = 1;
}
if (nnisclass && !variadic && !specialized && (ParmList_len($7) > ParmList_len(tparms))) {
Swig_error(cparse_file, cparse_line, "Too many template parameters. Maximum of %d.\n", ParmList_len(tparms));
} else if (nnisclass && !specialized && ((ParmList_len($7) < (ParmList_numrequired(tparms) - (variadic?1:0))))) { /* Variadic parameter is optional */
Swig_error(cparse_file, cparse_line, "Not enough template parameters specified. %d required.\n", (ParmList_numrequired(tparms)-(variadic?1:0)) );
} else if (!nnisclass && ((ParmList_len($7) != ParmList_len(tparms)))) {
/* must be an overloaded templated method - ignore it as it is overloaded with a different number of template parameters */
nn = Getattr(nn,"sym:nextSibling"); /* repeat for overloaded templated functions */
continue;
} else {
String *tname = Copy($5); String *tname = Copy($5);
Node *primary_template = Swig_symbol_clookup(tname, 0); Node *primary_template = Swig_symbol_clookup(tname, 0);

View File

@ -691,7 +691,7 @@ static EMatch does_parm_match(SwigType *type, SwigType *partial_parm_type, const
* Search for a template that matches name with given parameters. * Search for a template that matches name with given parameters.
* ----------------------------------------------------------------------------- */ * ----------------------------------------------------------------------------- */
static Node *template_locate(String *name, Parm *tparms, String *symname, Symtab *tscope) { static Node *template_locate(String *name, Parm *instantiated_parms, String *symname, Symtab *tscope) {
Node *n = 0; Node *n = 0;
String *tname = 0; String *tname = 0;
Node *templ; Node *templ;
@ -705,12 +705,9 @@ static Node *template_locate(String *name, Parm *tparms, String *symname, Symtab
int max_possible_partials = 0; int max_possible_partials = 0;
int posslen = 0; int posslen = 0;
/* Search for primary (unspecialized) template */
templ = Swig_symbol_clookup(name, 0);
if (template_debug) { if (template_debug) {
tname = Copy(name); tname = Copy(name);
SwigType_add_template(tname, tparms); SwigType_add_template(tname, instantiated_parms);
Printf(stdout, "\n"); Printf(stdout, "\n");
if (symname) if (symname)
Swig_diagnostic(cparse_file, cparse_line, "Template debug: Searching for match to: '%s' for instantiation of template named '%s'\n", tname, symname); Swig_diagnostic(cparse_file, cparse_line, "Template debug: Searching for match to: '%s' for instantiation of template named '%s'\n", tname, symname);
@ -720,9 +717,16 @@ static Node *template_locate(String *name, Parm *tparms, String *symname, Symtab
tname = 0; tname = 0;
} }
/* Search for primary (unspecialized) template */
templ = Swig_symbol_clookup(name, 0);
if (templ) { if (templ) {
if (template_debug) {
Printf(stdout, " found primary template <%s> '%s'\n", ParmList_str_defaultargs(Getattr(templ, "templateparms")), Getattr(templ, "name"));
}
tname = Copy(name); tname = Copy(name);
parms = CopyParmList(tparms); parms = CopyParmList(instantiated_parms);
/* All template specializations must be in the primary template's scope, store the symbol table for this scope for specialization lookups */ /* 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"); primary_scope = Getattr(templ, "sym:symtab");
@ -788,7 +792,7 @@ static Node *template_locate(String *name, Parm *tparms, String *symname, Symtab
String *previous_name = Getattr(previous_named_instantiation, "name"); String *previous_name = Getattr(previous_named_instantiation, "name");
String *previous_symname = Getattr(previous_named_instantiation, "sym:name"); String *previous_symname = Getattr(previous_named_instantiation, "sym:name");
String *unprocessed_tname = Copy(name); String *unprocessed_tname = Copy(name);
SwigType_add_template(unprocessed_tname, tparms); SwigType_add_template(unprocessed_tname, instantiated_parms);
if (template_debug) if (template_debug)
Printf(stdout, " previous instantiation with name '%s' found: '%s' - duplicate instantiation ignored\n", previous_symname, Getattr(n, "name")); Printf(stdout, " previous instantiation with name '%s' found: '%s' - duplicate instantiation ignored\n", previous_symname, Getattr(n, "name"));
@ -1040,14 +1044,15 @@ success:
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* Swig_cparse_template_locate() * Swig_cparse_template_locate()
* *
* Search for a template that matches name with given parameters. * Search for a template that matches name with given parameters and mark it for instantiation.
* For templated classes finds the specialized template should there be one. * For templated classes marks the specialized template should there be one.
* For templated functions finds the unspecialized template even if a specialized * For templated functions marks all the unspecialized templates even if specialized
* template exists. * templates exists.
* ----------------------------------------------------------------------------- */ * ----------------------------------------------------------------------------- */
Node *Swig_cparse_template_locate(String *name, Parm *tparms, String *symname, Symtab *tscope) { Node *Swig_cparse_template_locate(String *name, Parm *instantiated_parms, String *symname, Symtab *tscope) {
Node *n = template_locate(name, tparms, symname, tscope); /* this function does what we want for templated classes */ Node *match = 0;
Node *n = template_locate(name, instantiated_parms, symname, tscope); /* this function does what we want for templated classes */
if (n) { if (n) {
String *nodeType = nodeType(n); String *nodeType = nodeType(n);
@ -1055,7 +1060,22 @@ Node *Swig_cparse_template_locate(String *name, Parm *tparms, String *symname, S
assert(Equal(nodeType, "template")); assert(Equal(nodeType, "template"));
(void)nodeType; (void)nodeType;
isclass = (Equal(Getattr(n, "templatetype"), "class")); isclass = (Equal(Getattr(n, "templatetype"), "class"));
if (!isclass) {
if (isclass) {
Parm *tparmsfound = Getattr(n, "templateparms");
int specialized = !tparmsfound; /* fully specialized (an explicit specialization) */
int variadic = ParmList_variadic_parm(tparmsfound) != 0;
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));
} 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)) );
}
}
SetFlag(n, "instantiate");
match = n;
} else {
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.
The template found is not necessarily the one we want when dealing with templated The template found is not necessarily the one we want when dealing with templated
functions. We don't want any specialized templated functions as they won't have functions. We don't want any specialized templated functions as they won't have
@ -1064,32 +1084,63 @@ Node *Swig_cparse_template_locate(String *name, Parm *tparms, String *symname, S
templated function with different numbers of template parameters. */ templated function with different numbers of template parameters. */
if (template_debug) { if (template_debug) {
Printf(stdout, " Not a templated class, seeking most appropriate templated function\n"); Printf(stdout, " Not a templated class, seeking all appropriate primary templated functions\n");
} }
n = Swig_symbol_clookup_local(name, 0); firstn = Swig_symbol_clookup_local(name, 0);
n = firstn;
/* First look for all overloaded functions (non-variadic) template matches.
* Looking for all template parameter matches only (not function parameter matches)
* as %template instantiation uses template parameters without any function parameters. */
while (n) { while (n) {
Parm *tparmsfound = Getattr(n, "templateparms"); if (Strcmp(nodeType(n), "template") == 0) {
if (ParmList_len(tparms) == ParmList_len(tparmsfound)) { Parm *tparmsfound = Getattr(n, "templateparms");
/* successful match */ if (!ParmList_variadic_parm(tparmsfound)) {
break; if (ParmList_len(instantiated_parms) == ParmList_len(tparmsfound)) {
/* successful match */
if (template_debug) {
Printf(stdout, " found: template <%s> '%s' (%s)\n", ParmList_str_defaultargs(Getattr(n, "templateparms")), name, ParmList_str_defaultargs(Getattr(n, "parms")));
}
SetFlag(n, "instantiate");
if (!match)
match = n; /* first match */
}
}
} }
/* repeat until we find a match with correct number of templated parameters */ /* repeat to find all matches with correct number of templated parameters */
n = Getattr(n, "sym:nextSibling"); n = Getattr(n, "sym:nextSibling");
} }
if (!n) { /* Only consider variadic templates if there are no non-variadic template matches */
Swig_error(cparse_file, cparse_line, "Template '%s' undefined.\n", name); if (!match) {
n = firstn;
while (n) {
if (Strcmp(nodeType(n), "template") == 0) {
Parm *tparmsfound = Getattr(n, "templateparms");
if (ParmList_variadic_parm(tparmsfound)) {
if (ParmList_len(instantiated_parms) >= ParmList_len(tparmsfound) - 1) {
/* successful variadic match */
if (template_debug) {
Printf(stdout, " found: template <%s> '%s' (%s)\n", ParmList_str_defaultargs(Getattr(n, "templateparms")), name, ParmList_str_defaultargs(Getattr(n, "parms")));
}
SetFlag(n, "instantiate");
if (!match)
match = n; /* first match */
}
}
}
/* repeat to find all matches with correct number of templated parameters */
n = Getattr(n, "sym:nextSibling");
}
} }
if ((template_debug) && (n)) { if (!match) {
Printf(stdout, "Templated function found: %p\n", n); Swig_error(cparse_file, cparse_line, "Template '%s' undefined.\n", name);
Swig_print_node(n);
} }
} }
} }
return n; return match;
} }
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
@ -1164,24 +1215,24 @@ static void expand_defaults(ParmList *expanded_templateparms) {
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* Swig_cparse_template_parms_expand() * Swig_cparse_template_parms_expand()
* *
* instantiated_parameters: template parameters passed to %template * instantiated_parms: template parameters passed to %template
* primary: primary template node * primary: primary template node
* *
* Expand the instantiated_parameters and return a parameter list with default * Expand the instantiated_parms and return a parameter list with default
* arguments filled in where necessary. * arguments filled in where necessary.
* ----------------------------------------------------------------------------- */ * ----------------------------------------------------------------------------- */
ParmList *Swig_cparse_template_parms_expand(ParmList *instantiated_parameters, Node *primary) { ParmList *Swig_cparse_template_parms_expand(ParmList *instantiated_parms, Node *primary) {
ParmList *expanded_templateparms = 0; ParmList *expanded_templateparms = 0;
ParmList *templateparms = Getattr(primary, "templateparms"); ParmList *templateparms = Getattr(primary, "templateparms");
if (Equal(Getattr(primary, "templatetype"), "class")) { if (Equal(Getattr(primary, "templatetype"), "class")) {
/* Templated class */ /* Templated class */
expanded_templateparms = CopyParmList(instantiated_parameters); 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) {
ParmList *defaults_start = ParmList_nth_parm(templateparms, ParmList_len(instantiated_parameters)); ParmList *defaults_start = ParmList_nth_parm(templateparms, ParmList_len(instantiated_parms));
if (defaults_start) { if (defaults_start) {
ParmList *defaults = CopyParmList(defaults_start); ParmList *defaults = CopyParmList(defaults_start);
mark_defaults(defaults); mark_defaults(defaults);
@ -1192,7 +1243,7 @@ ParmList *Swig_cparse_template_parms_expand(ParmList *instantiated_parameters, N
} else { } else {
/* 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 */
expanded_templateparms = CopyParmList(instantiated_parameters); expanded_templateparms = CopyParmList(instantiated_parms);
merge_parameters(expanded_templateparms, templateparms); merge_parameters(expanded_templateparms, templateparms);
} }