From 2fc0edc4fd9da1c25254127128aaa601cad30916 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Tue, 3 Jan 2023 22:34:59 +0000 Subject: [PATCH] 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. --- CHANGES.current | 7 +- .../cpp11_variadic_function_templates.i | 18 ++- ...cpp11_variadic_function_templates_runme.py | 20 +++ Source/CParse/parser.y | 25 +--- Source/CParse/templ.c | 117 +++++++++++++----- 5 files changed, 132 insertions(+), 55 deletions(-) create mode 100644 Examples/test-suite/python/cpp11_variadic_function_templates_runme.py diff --git a/CHANGES.current b/CHANGES.current index 4cdfee90c..6213afcf1 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -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 diff --git a/Examples/test-suite/cpp11_variadic_function_templates.i b/Examples/test-suite/cpp11_variadic_function_templates.i index f95c57a1a..82836a4f3 100644 --- a/Examples/test-suite/cpp11_variadic_function_templates.i +++ b/Examples/test-suite/cpp11_variadic_function_templates.i @@ -78,5 +78,19 @@ void emplace(Args &&... args) noexcept( %} %template(emplace) EmplaceContainer::emplace; -// TODO -//%template(emplace) EmplaceContainer::emplace; +%template(emplace) EmplaceContainer::emplace; +%template(emplace) EmplaceContainer::emplace; +%template(emplace) EmplaceContainer::emplace; + + +// Overloading mix of variadic and non-variadic templates +%inline %{ +template int variadicmix1(T t, U u) { return 10; } +template int variadicmix1(T... t) { return 20; } +%} + +%template(variadicmix1) variadicmix1<>; +%template(variadicmix1) variadicmix1; +%template(variadicmix1) variadicmix1; +%template(variadicmix1) variadicmix1; +%template(variadicmix1) variadicmix1; diff --git a/Examples/test-suite/python/cpp11_variadic_function_templates_runme.py b/Examples/test-suite/python/cpp11_variadic_function_templates_runme.py new file mode 100644 index 000000000..ae1febb46 --- /dev/null +++ b/Examples/test-suite/python/cpp11_variadic_function_templates_runme.py @@ -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) diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index 734e53a34..c66b60db6 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -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); diff --git a/Source/CParse/templ.c b/Source/CParse/templ.c index 1eeb91774..7ab91c145 100644 --- a/Source/CParse/templ.c +++ b/Source/CParse/templ.c @@ -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) { - Parm *tparmsfound = Getattr(n, "templateparms"); - if (ParmList_len(tparms) == ParmList_len(tparmsfound)) { - /* successful match */ - break; + if (Strcmp(nodeType(n), "template") == 0) { + Parm *tparmsfound = Getattr(n, "templateparms"); + if (!ParmList_variadic_parm(tparmsfound)) { + 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"); } - if (!n) { - Swig_error(cparse_file, cparse_line, "Template '%s' undefined.\n", name); + /* 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 ((template_debug) && (n)) { - Printf(stdout, "Templated function found: %p\n", n); - Swig_print_node(n); + if (!match) { + Swig_error(cparse_file, cparse_line, "Template '%s' undefined.\n", name); } } } - 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); }