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)
===========================
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
#1863 Syntax error fixes parsing more elaborate parameter pack arguments that are
used in function pointers, member function pointers:
@ -50,7 +55,7 @@ Version 4.2.0 (in progress)
are function pointers.
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.
2022-12-06: wsfulton

View File

@ -78,5 +78,19 @@ void emplace(Args &&... args) noexcept(
%}
%template(emplace) EmplaceContainer::emplace<int,A>;
// TODO
//%template(emplace) EmplaceContainer::emplace<int,A,B,C>;
%template(emplace) EmplaceContainer::emplace<int,A,B>;
%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 *outer_class = currentOuterClass;
Symtab *tscope = 0;
int specialized = 0; /* fully specialized (an explicit specialization) */
int variadic = 0;
String *symname = $3 ? NewString($3) : 0;
$$ = 0;
@ -2875,23 +2873,12 @@ template_directive: SWIGTEMPLATE LPAREN idstringopt RPAREN idcolonnt LESSTHAN va
Node *linkliststart = 0;
while (nn) {
Node *templnode = 0;
if (Strcmp(nodeType(nn),"template") == 0) {
int nnisclass = (Strcmp(Getattr(nn,"templatetype"),"class") == 0); /* if not a templated class it is a templated function */
Parm *tparms = Getattr(nn,"templateparms");
if (!tparms) {
specialized = 1;
} else if (ParmList_variadic_parm(tparms)) {
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 {
if (GetFlag(nn, "instantiate")) {
Delattr(nn, "instantiate");
{
int nnisclass = (Strcmp(Getattr(nn, "templatetype"), "class") == 0); /* if not a templated class it is a templated function */
Parm *tparms = Getattr(nn, "templateparms");
int specialized = !tparms; /* fully specialized (an explicit specialization) */
String *tname = Copy($5);
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.
* ----------------------------------------------------------------------------- */
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;
String *tname = 0;
Node *templ;
@ -705,12 +705,9 @@ static Node *template_locate(String *name, Parm *tparms, String *symname, Symtab
int max_possible_partials = 0;
int posslen = 0;
/* Search for primary (unspecialized) template */
templ = Swig_symbol_clookup(name, 0);
if (template_debug) {
tname = Copy(name);
SwigType_add_template(tname, tparms);
SwigType_add_template(tname, instantiated_parms);
Printf(stdout, "\n");
if (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;
}
/* Search for primary (unspecialized) template */
templ = Swig_symbol_clookup(name, 0);
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);
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 */
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_symname = Getattr(previous_named_instantiation, "sym:name");
String *unprocessed_tname = Copy(name);
SwigType_add_template(unprocessed_tname, tparms);
SwigType_add_template(unprocessed_tname, instantiated_parms);
if (template_debug)
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()
*
* Search for a template that matches name with given parameters.
* For templated classes finds the specialized template should there be one.
* For templated functions finds the unspecialized template even if a specialized
* template exists.
* Search for a template that matches name with given parameters and mark it for instantiation.
* For templated classes marks the specialized template should there be one.
* For templated functions marks all the unspecialized templates even if specialized
* templates exists.
* ----------------------------------------------------------------------------- */
Node *Swig_cparse_template_locate(String *name, Parm *tparms, String *symname, Symtab *tscope) {
Node *n = template_locate(name, tparms, symname, tscope); /* this function does what we want for templated classes */
Node *Swig_cparse_template_locate(String *name, Parm *instantiated_parms, String *symname, Symtab *tscope) {
Node *match = 0;
Node *n = template_locate(name, instantiated_parms, symname, tscope); /* this function does what we want for templated classes */
if (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"));
(void)nodeType;
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.
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
@ -1064,32 +1084,63 @@ Node *Swig_cparse_template_locate(String *name, Parm *tparms, String *symname, S
templated function with different numbers of template parameters. */
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) {
if (Strcmp(nodeType(n), "template") == 0) {
Parm *tparmsfound = Getattr(n, "templateparms");
if (ParmList_len(tparms) == ParmList_len(tparmsfound)) {
if (!ParmList_variadic_parm(tparmsfound)) {
if (ParmList_len(instantiated_parms) == ParmList_len(tparmsfound)) {
/* successful match */
break;
if (template_debug) {
Printf(stdout, " found: template <%s> '%s' (%s)\n", ParmList_str_defaultargs(Getattr(n, "templateparms")), name, ParmList_str_defaultargs(Getattr(n, "parms")));
}
/* repeat until we find a match with correct number of templated parameters */
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 (!n) {
/* Only consider variadic templates if there are no non-variadic template matches */
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 (!match) {
Swig_error(cparse_file, cparse_line, "Template '%s' undefined.\n", name);
}
if ((template_debug) && (n)) {
Printf(stdout, "Templated function found: %p\n", n);
Swig_print_node(n);
}
}
}
return n;
return match;
}
/* -----------------------------------------------------------------------------
@ -1164,24 +1215,24 @@ static void expand_defaults(ParmList *expanded_templateparms) {
/* -----------------------------------------------------------------------------
* Swig_cparse_template_parms_expand()
*
* instantiated_parameters: template parameters passed to %template
* instantiated_parms: template parameters passed to %template
* 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.
* ----------------------------------------------------------------------------- */
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 *templateparms = Getattr(primary, "templateparms");
if (Equal(Getattr(primary, "templatetype"), "class")) {
/* Templated class */
expanded_templateparms = CopyParmList(instantiated_parameters);
expanded_templateparms = CopyParmList(instantiated_parms);
int variadic = merge_parameters(expanded_templateparms, templateparms);
/* Add default arguments from primary template */
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) {
ParmList *defaults = CopyParmList(defaults_start);
mark_defaults(defaults);
@ -1192,7 +1243,7 @@ ParmList *Swig_cparse_template_parms_expand(ParmList *instantiated_parameters, N
} else {
/* Templated function */
/* 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);
}