From d0fc5b7b5bcf83d6af804d13f209e1c6f33f5ee3 Mon Sep 17 00:00:00 2001 From: Lior Goldberg Date: Fri, 1 Jul 2016 16:15:40 +0300 Subject: [PATCH] Add C++11 alias templates --- Doc/Manual/CPlusPlus11.html | 19 ++-- Examples/test-suite/cpp11_template_typedefs.i | 98 ++++++++++++------- .../python/cpp11_template_typedefs_runme.py | 43 ++++++++ Source/CParse/parser.y | 17 ++-- Source/Modules/lang.cxx | 2 +- Source/Modules/typepass.cxx | 4 + 6 files changed, 128 insertions(+), 55 deletions(-) create mode 100644 Examples/test-suite/python/cpp11_template_typedefs_runme.py diff --git a/Doc/Manual/CPlusPlus11.html b/Doc/Manual/CPlusPlus11.html index a4845832e..2f3783327 100644 --- a/Doc/Manual/CPlusPlus11.html +++ b/Doc/Manual/CPlusPlus11.html @@ -622,20 +622,15 @@ which is equivalent to the old style typedef: typedef void (*PFD)(double); // The old style -

-SWIG supports type aliasing. -

-

The following is an example of an alias template:

-template< typename T1, typename T2, int >
+template< typename T1, typename T2, int N >
 class SomeType {
 public:
   T1 a;
   T2 b;
-  int c;
 };
 
 template< typename T2 >
@@ -643,14 +638,14 @@ using TypedefName = SomeType<char*, T2, 5>;
 

-These are partially supported as SWIG will parse these and identify them, however, they are ignored as they are not added to the type system. A warning such as the following is issued: +SWIG supports both type aliasing and alias templates. +However, in order to use an alias template, the %template directive must be used:

-
-
-example.i:13: Warning 342: The 'using' keyword in template aliasing is not fully supported yet.
-
-
+
+%template(SomeTypeBool) SomeType<char*, bool, 5>;
+%template() TypedefName<bool>;
+

7.2.17 Unrestricted unions

diff --git a/Examples/test-suite/cpp11_template_typedefs.i b/Examples/test-suite/cpp11_template_typedefs.i index 97a1da7ed..02cb8ac30 100644 --- a/Examples/test-suite/cpp11_template_typedefs.i +++ b/Examples/test-suite/cpp11_template_typedefs.i @@ -1,56 +1,86 @@ -/* This testcase checks whether SWIG correctly parses alias templates. */ +/* This testcase checks whether SWIG correctly handles alias templates. */ %module cpp11_template_typedefs -%warnfilter(SWIGWARN_CPP11_ALIAS_TEMPLATE) TypedefName; -%warnfilter(SWIGWARN_CPP11_ALIAS_TEMPLATE) TypedefNamePtr; -%warnfilter(SWIGWARN_CPP11_ALIAS_TEMPLATE) MyIntKeyClass; -%warnfilter(SWIGWARN_CPP11_ALIAS_DECLARATION) PF; -%warnfilter(SWIGWARN_CPP11_ALIAS_DECLARATION) BucketAllocator1; -%warnfilter(SWIGWARN_CPP11_ALIAS_DECLARATION) BucketAllocator2; - -// This warning should go away when type aliasing is supported -#pragma SWIG nowarn=SWIGWARN_PARSE_USING_UNDEF // Nothing known about 'p.SomeType< char *,T2,4 >'. - %inline %{ -template< typename T1, typename T2, int > + +template +using ptr_t = T*; + +namespace ns { + +template class SomeType { +public: + using type1_t = T1; + using type2_t = T2; T1 a; T2 b; - int c; + constexpr int get_n() { return N; } }; -// template aliasing -template< typename T2 > -using TypedefName = SomeType; -template< typename T2 > -using TypedefNamePtr = SomeType*; - -// type aliasing -typedef void (*PFD)(double); // Old style -using PF = void (*)(double); // New introduced syntax - - -// use of template aliasing -template -class MyCPP11Class { +// Specialization for T1=const char*, T2=bool +template +class SomeType { +public: + using type1_t = const char*; + using type2_t = bool; + type1_t a; + type2_t b; + constexpr int get_n() { return 3 * N; } }; -template using MyIntKeyClass = MyCPP11Class; -MyIntKeyClass intchar; -TypedefName alias1(TypedefName a) { return a; } -TypedefNamePtr alias1(TypedefNamePtr a = nullptr) { return a; } -%} +// alias templates +template +using TypedefName = SomeType; +template +using TypedefNamePtr = ptr_t>; + +// alias template that returns T2 for a SomeType class +template +using T2_of = typename T::type2_t; + +T2_of> get_SomeType_b(const SomeType& x) { return x.b; } + +template +T2_of> get_SomeType_b2(const TypedefName& x) { return x.b; } + +} // namespace ns + +ns::TypedefName create_TypedefName() { return { "hello", 10}; } +ns::TypedefName create_TypedefNameBool() { return { "hello", true}; } +ns::TypedefNamePtr identity(ns::TypedefNamePtr a = nullptr) { return a; } -%inline %{ typedef double Val; template struct ListBucket { }; namespace Alloc { template struct rebind { - typedef int other; + using other = int; }; } using BucketAllocator1 = typename Alloc::template rebind>::other; using BucketAllocator2 = typename Alloc::template rebind<::template ListBucket>::other; + +BucketAllocator1 get_bucket_allocator1() { return 1; } +BucketAllocator2 get_bucket_allocator2() { return 2; } %} + +%immutable ns::SomeType::a; + +// %template() directives + +%template(SomeTypeInt5) ns::SomeType; +%template(SomeTypeInt4) ns::SomeType; +%template(SomeTypeBool5) ns::SomeType; + +%template(ListBucketDouble) ListBucket; +%template(RebindListBucketDouble) Alloc::rebind>; + +%template() ptr_t>; +%template() ns::TypedefName; +%template() ns::TypedefName; +%template() ns::TypedefNamePtr; +%template() ns::T2_of>; + +%template(get_SomeType_b2) ns::get_SomeType_b2; diff --git a/Examples/test-suite/python/cpp11_template_typedefs_runme.py b/Examples/test-suite/python/cpp11_template_typedefs_runme.py new file mode 100644 index 000000000..4d6fc2629 --- /dev/null +++ b/Examples/test-suite/python/cpp11_template_typedefs_runme.py @@ -0,0 +1,43 @@ +from cpp11_template_typedefs import * + +t = create_TypedefName() +if type(t).__name__ != "SomeTypeInt5": + raise RuntimeError("type(t) is '%s' and should be 'SomeTypeInt5'" % type(t).__name__) + +if t.a != "hello": + raise RuntimeError("t.a should be 'hello'") +if t.b != 10: + raise RuntimeError("t.b should be 10") +if t.get_n() != 5: + raise RuntimeError("t.get_n() should be 5") + +t_bool = create_TypedefNameBool() +if type(t_bool).__name__ != "SomeTypeBool5": + raise RuntimeError("type(t_bool) is '%s' and should be 'SomeTypeBool5'" % type(t_bool).__name__) + +if t_bool.a != "hello": + raise RuntimeError("t_bool.a should be 'hello'") +if t_bool.b != True: + raise RuntimeError("t_bool.b should be True") +if t_bool.get_n() != 15: + raise RuntimeError("t_bool.get_n() should be 15") + +if get_SomeType_b(t) != 10: + raise RuntimeError("get_SomeType_b(t) should be 10") + +if get_SomeType_b2(t) != 10: + raise RuntimeError("get_SomeType_b2(t) should be 10") + +t2 = SomeTypeInt4() +t2.b = 0 +t3 = identity(t2) +t3.b = 5 +if t2.b != 5: + raise RuntimeError("t2.b should be 5") + +if get_bucket_allocator1() != 1: + raise RuntimeError("bucket_allocator1 should be 1") + +# SWIG doesn't handle ::MyClass as a template argument. Skip this test. +#if get_bucket_allocator2() != 2: +# raise RuntimeError("bucket_allocator2 should be 2") diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index 784187c28..4b06d3bd3 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -2910,16 +2910,17 @@ c_declaration : c_decl { add_symbols($$); } | TEMPLATE LESSTHAN template_parms GREATERTHAN USING idcolon EQUAL type plain_declarator SEMI { - $$ = new_node("using"); - Setattr($$,"name",$6); + /* Convert alias template to a "template" typedef statement */ + $$ = new_node("template"); SwigType_push($8,$9.type); - Setattr($$,"uname",$8); + Setattr($$,"type",$8); + Setattr($$,"storage","typedef"); + Setattr($$,"name",$6); + Setattr($$,"decl",""); + Setattr($$,"templateparms",$3); + Setattr($$,"templatetype","cdecl"); + SetFlag($$,"aliastemplate"); add_symbols($$); - SWIG_WARN_NODE_BEGIN($$); - Swig_warning(WARN_CPP11_ALIAS_TEMPLATE, cparse_file, cparse_line, "The 'using' keyword in template aliasing is not fully supported yet.\n"); - SWIG_WARN_NODE_END($$); - - $$ = 0; /* TODO - ignored for now */ } ; diff --git a/Source/Modules/lang.cxx b/Source/Modules/lang.cxx index 9b1173443..57b0d50e6 100644 --- a/Source/Modules/lang.cxx +++ b/Source/Modules/lang.cxx @@ -870,7 +870,7 @@ int Language::cDeclaration(Node *n) { } else { // Found an unignored templated method that has an empty template instantiation (%template()) // Ignore it unless it has been %rename'd - if (Strncmp(symname, "__dummy_", 8) == 0) { + if (Strncmp(symname, "__dummy_", 8) == 0 && Cmp(storage, "typedef") != 0) { SetFlag(n, "feature:ignore"); Swig_warning(WARN_LANG_TEMPLATE_METHOD_IGNORE, input_file, line_number, "%%template() contains no name. Template method ignored: %s\n", Swig_name_decl(n)); diff --git a/Source/Modules/typepass.cxx b/Source/Modules/typepass.cxx index dc4d02bdd..bf8028c29 100644 --- a/Source/Modules/typepass.cxx +++ b/Source/Modules/typepass.cxx @@ -566,6 +566,10 @@ class TypePass:private Dispatcher { SwigType_typedef_class(rname); Delete(rname); /* SwigType_typedef_class(name); */ + } else if (Strcmp(ttype, "cdecl") == 0) { + String *rname = SwigType_typedef_resolve_all(name); + SwigType_typedef_class(rname); + Delete(rname); } return SWIG_OK; }