diff --git a/Doc/Manual/CPlusPlus11.html b/Doc/Manual/CPlusPlus11.html
index 0d2f22aff6f..9d315d8e35c 100644
--- a/Doc/Manual/CPlusPlus11.html
+++ b/Doc/Manual/CPlusPlus11.html
@@ -414,6 +414,29 @@
7.2.10 Object cons
};
+Explicit overrides and final
+
+
+The special identifiers final and override can be used on methods and destructors,
+such as in the following example:
+
+
+
+struct BaseStruct {
+ virtual void ab() const = 0;
+ virtual void cd();
+ virtual void ef();
+ virtual ~BaseStruct();
+};
+struct DerivedStruct : BaseStruct {
+ virtual void ab() const override;
+ virtual void cd() final;
+ virtual void ef() final override;
+ virtual ~DerivedStruct() override;
+};
+
+
+
7.2.11 Null pointer constant
diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk
index 642e7fd8cc2..fb53a0ec9b6 100644
--- a/Examples/test-suite/common.mk
+++ b/Examples/test-suite/common.mk
@@ -490,6 +490,7 @@ CPP11_TEST_CASES = \
cpp11_default_delete \
cpp11_delegating_constructors \
cpp11_explicit_conversion_operators \
+ cpp11_final_override \
cpp11_function_objects \
cpp11_inheriting_constructors \
cpp11_initializer_list \
diff --git a/Examples/test-suite/cpp11_final_override.i b/Examples/test-suite/cpp11_final_override.i
new file mode 100644
index 00000000000..f691f8770a1
--- /dev/null
+++ b/Examples/test-suite/cpp11_final_override.i
@@ -0,0 +1,128 @@
+// Test C++11 virtual specifier sequences (final and/or override on methods)
+// Also check final/override - the two 'identifiers with special meaning' work as normal identifiers
+
+%module cpp11_final_override
+
+%warnfilter(SWIGWARN_PARSE_KEYWORD) final; // 'final' is a java keyword, renaming to '_final'
+%warnfilter(SWIGWARN_PARSE_KEYWORD) override; // 'override' is a C# keyword, renaming to '_override'
+
+%inline %{
+
+struct Base {
+ virtual void stuff() const {}
+ virtual void override1() const {}
+ virtual void override2() const {}
+ virtual void finaloverride1() {}
+ virtual void finaloverride2() {}
+ virtual void finaloverride3() {}
+ virtual void finaloverride4() const {}
+ virtual ~Base() {}
+};
+
+struct Derived /*final*/ : Base {
+ virtual void stuff() const noexcept override final {}
+ virtual void override1() const noexcept override;
+ virtual void override2() const noexcept override;
+ virtual void final1() final {}
+ virtual void final2() noexcept final {}
+ virtual void final4() const final {}
+ virtual void final5() const noexcept final {}
+ virtual void finaloverride1() final override {}
+ virtual void finaloverride2() override final {}
+ virtual void finaloverride3() noexcept override final {}
+ virtual void finaloverride4() const noexcept override final {}
+ virtual ~Derived() override final {}
+};
+void Derived::override2() const noexcept {}
+
+// Pure virtual methods
+struct PureBase {
+ virtual void pure1(int) const = 0;
+ virtual void pure2(int) const = 0;
+ virtual void pure3(int) const = 0;
+ virtual void pure4(int) const = 0;
+ virtual void pure5(int) const = 0;
+ virtual ~PureBase() {}
+};
+
+struct PureDerived : PureBase {
+ virtual void pure1(int) const override = 0;
+ virtual void pure2(int) const final = 0;
+ virtual void pure3(int) const override final = 0;
+ virtual void pure4(int) const final override = 0;
+ virtual void pure5(int) const noexcept final override = 0;
+ virtual ~PureDerived() override final;
+};
+void PureDerived::pure1(int) const {}
+void PureDerived::pure2(int) const {}
+void PureDerived::pure3(int) const {}
+void PureDerived::pure4(int) const {}
+void PureDerived::pure5(int) const noexcept {}
+PureDerived::~PureDerived() {}
+
+// Destructors and virtual specifier sequences (final/override)
+struct Destructors1 : Base {
+ virtual ~Destructors1() override {}
+};
+struct Destructors2 : Base {
+ virtual ~Destructors2() final {}
+};
+struct Destructors3 : Base {
+ virtual ~Destructors3() noexcept final override {}
+};
+struct Destructors4 : Base {
+ virtual ~Destructors4() noexcept override final {}
+};
+
+// Check the two 'identifiers with special meaning' work as normal identifiers
+struct FinalOverrideMethods {
+ virtual void final() {}
+ virtual void override(int) {}
+};
+struct FinalOverrideVariables {
+ int final;
+ double override;
+};
+void final(int) {}
+void override() {}
+%}
+
+%{
+void Derived::override1() const noexcept {}
+%}
+
+// Example in documentation ... declarations only
+%inline %{
+struct BaseStruct {
+ virtual void ab() const = 0;
+ virtual void cd();
+ virtual void ef();
+ virtual ~BaseStruct();
+};
+struct DerivedStruct : BaseStruct {
+ virtual void ab() const override;
+ virtual void cd() final;
+ virtual void ef() final override;
+ virtual ~DerivedStruct() override;
+};
+struct DerivedNoVirtualStruct : BaseStruct {
+ void ab() const override;
+ void cd() final;
+ void ef() final override;
+ ~DerivedNoVirtualStruct() override;
+};
+%}
+
+%{
+void BaseStruct::cd() {}
+void BaseStruct::ef() {}
+BaseStruct::~BaseStruct() {}
+void DerivedStruct::ab() const {}
+void DerivedStruct::cd() {}
+void DerivedStruct::ef() {}
+DerivedStruct::~DerivedStruct() {}
+void DerivedNoVirtualStruct::ab() const {}
+void DerivedNoVirtualStruct::cd() {}
+void DerivedNoVirtualStruct::ef() {}
+DerivedNoVirtualStruct::~DerivedNoVirtualStruct() {}
+%}
diff --git a/Source/CParse/cscanner.c b/Source/CParse/cscanner.c
index 2dfc2c47951..4e02b4b6222 100644
--- a/Source/CParse/cscanner.c
+++ b/Source/CParse/cscanner.c
@@ -748,18 +748,18 @@ int yylex(void) {
yylval.intvalue = cparse_line;
return (TEMPLATE);
}
- if (strcmp(yytext, "delete") == 0) {
+ if (strcmp(yytext, "delete") == 0)
return (DELETE_KW);
- }
- if (strcmp(yytext, "default") == 0) {
+ if (strcmp(yytext, "default") == 0)
return (DEFAULT);
- }
- if (strcmp(yytext, "using") == 0) {
+ if (strcmp(yytext, "using") == 0)
return (USING);
- }
- if (strcmp(yytext, "namespace") == 0) {
+ if (strcmp(yytext, "namespace") == 0)
return (NAMESPACE);
- }
+ if (strcmp(yytext, "override") == 0)
+ return (OVERRIDE);
+ if (strcmp(yytext, "final") == 0)
+ return (FINAL);
} else {
if (strcmp(yytext, "class") == 0) {
Swig_warning(WARN_PARSE_CLASS_KEYWORD, cparse_file, cparse_line, "class keyword used, but not in C++ mode.\n");
diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y
index a7604ff56de..fe8b7f97f53 100644
--- a/Source/CParse/parser.y
+++ b/Source/CParse/parser.y
@@ -1388,8 +1388,9 @@ static void mark_nodes_as_extend(Node *n) {
%token ILLEGAL CONSTANT
%token NAME RENAME NAMEWARN EXTEND PRAGMA FEATURE VARARGS
%token ENUM
-%token CLASS TYPENAME PRIVATE PUBLIC PROTECTED COLON STATIC VIRTUAL FRIEND THROW CATCH EXPLICIT AUTO NOEXCEPT
-%token STATIC_ASSERT CONSTEXPR THREAD_LOCAL DECLTYPE /* C++11 keywords */
+%token CLASS TYPENAME PRIVATE PUBLIC PROTECTED COLON STATIC VIRTUAL FRIEND THROW CATCH EXPLICIT
+%token STATIC_ASSERT CONSTEXPR THREAD_LOCAL DECLTYPE AUTO NOEXCEPT /* C++11 keywords */
+%token OVERRIDE FINAL /* C++11 identifiers with special meaning */
%token USING
%token NAMESPACE
%token NATIVE INLINE
@@ -1443,6 +1444,7 @@ static void mark_nodes_as_extend(Node *n) {
%type kwargs options;
/* Misc */
+%type identifier;
%type initializer cpp_const exception_specification;
%type storage_class;
%type parms ptail rawparms varargs_parms ;
@@ -1480,6 +1482,7 @@ static void mark_nodes_as_extend(Node *n) {
%type lambda_introducer lambda_body;
%type lambda_tail;
%type optional_constant_directive;
+%type virt_specifier_seq;
%%
@@ -1701,7 +1704,7 @@ clear_directive : CLEAR tm_list SEMI {
%constant type name = value;
------------------------------------------------------------ */
-constant_directive : CONSTANT ID EQUAL definetype SEMI {
+constant_directive : CONSTANT identifier EQUAL definetype SEMI {
if (($4.type != T_ERROR) && ($4.type != T_SYMBOL)) {
SwigType *type = NewSwigType($4.type);
$$ = new_node("constant");
@@ -1783,7 +1786,7 @@ echo_directive : ECHO HBLOCK {
%except;
------------------------------------------------------------ */
-except_directive : EXCEPT LPAREN ID RPAREN LBRACE {
+except_directive : EXCEPT LPAREN identifier RPAREN LBRACE {
skip_balanced('{','}');
$$ = 0;
Swig_warning(WARN_DEPRECATED_EXCEPT,cparse_file, cparse_line, "%%except is deprecated. Use %%exception instead.\n");
@@ -1795,7 +1798,7 @@ except_directive : EXCEPT LPAREN ID RPAREN LBRACE {
Swig_warning(WARN_DEPRECATED_EXCEPT,cparse_file, cparse_line, "%%except is deprecated. Use %%exception instead.\n");
}
- | EXCEPT LPAREN ID RPAREN SEMI {
+ | EXCEPT LPAREN identifier RPAREN SEMI {
$$ = 0;
Swig_warning(WARN_DEPRECATED_EXCEPT,cparse_file, cparse_line, "%%except is deprecated. Use %%exception instead.\n");
}
@@ -2080,13 +2083,13 @@ name_directive : NAME LPAREN idstring RPAREN {
%native(scriptname) type name (parms);
------------------------------------------------------------ */
-native_directive : NATIVE LPAREN ID RPAREN storage_class ID SEMI {
+native_directive : NATIVE LPAREN identifier RPAREN storage_class identifier SEMI {
$$ = new_node("native");
Setattr($$,"name",$3);
Setattr($$,"wrap:name",$6);
add_symbols($$);
}
- | NATIVE LPAREN ID RPAREN storage_class type declarator SEMI {
+ | NATIVE LPAREN identifier RPAREN storage_class type declarator SEMI {
if (!SwigType_isfunction($7.type)) {
Swig_error(cparse_file,cparse_line,"%%native declaration '%s' is not a function.\n", $7.id);
$$ = 0;
@@ -2112,13 +2115,13 @@ native_directive : NATIVE LPAREN ID RPAREN storage_class ID SEMI {
%pragma name
------------------------------------------------------------ */
-pragma_directive : PRAGMA pragma_lang ID EQUAL pragma_arg {
+pragma_directive : PRAGMA pragma_lang identifier EQUAL pragma_arg {
$$ = new_node("pragma");
Setattr($$,"lang",$2);
Setattr($$,"name",$3);
Setattr($$,"value",$5);
}
- | PRAGMA pragma_lang ID {
+ | PRAGMA pragma_lang identifier {
$$ = new_node("pragma");
Setattr($$,"lang",$2);
Setattr($$,"name",$3);
@@ -2129,13 +2132,12 @@ pragma_arg : string { $$ = NewString($1); }
| HBLOCK { $$ = $1; }
;
-pragma_lang : LPAREN ID RPAREN { $$ = $2; }
+pragma_lang : LPAREN identifier RPAREN { $$ = $2; }
| empty { $$ = (char *) "swig"; }
;
/* ------------------------------------------------------------
- %rename identifier newname;
- %rename identifier "newname";
+ %rename(newname) identifier;
------------------------------------------------------------ */
rename_directive : rename_namewarn declarator idstring SEMI {
@@ -2883,7 +2885,7 @@ c_declaration : c_decl {
$$ = 0; /* TODO - ignored for now */
}
- | TEMPLATE LESSTHAN template_parms GREATERTHAN USING idcolon EQUAL ID {
+ | TEMPLATE LESSTHAN template_parms GREATERTHAN USING idcolon EQUAL identifier {
skip_decl();
$$ = new_node("using");
Setattr($$,"uname",$8);
@@ -4221,7 +4223,7 @@ cpp_namespace_decl : NAMESPACE idcolon LBRACE {
Namespaceprefix = Swig_symbol_qualifiedscopename(0);
add_symbols($$);
}
- | NAMESPACE ID EQUAL idcolon SEMI {
+ | NAMESPACE identifier EQUAL idcolon SEMI {
/* Namespace alias */
Node *n;
$$ = new_node("namespace");
@@ -5810,7 +5812,7 @@ explicit_default : DEFAULT {
/* Some stuff for handling enums */
-ename : ID { $$ = $1; }
+ename : identifier { $$ = $1; }
| empty { $$ = (char *) 0;}
;
@@ -5837,7 +5839,7 @@ enumlist : enumlist COMMA optional_constant_directive edecl optional_cons
}
;
-edecl : ID {
+edecl : identifier {
SwigType *type = NewSwigType(T_INT);
$$ = new_node("enumitem");
Setattr($$,"name",$1);
@@ -5845,7 +5847,7 @@ edecl : ID {
SetFlag($$,"feature:immutable");
Delete(type);
}
- | ID EQUAL etype {
+ | identifier EQUAL etype {
SwigType *type = NewSwigType($3.type == T_BOOL ? T_BOOL : ($3.type == T_CHAR ? T_CHAR : T_INT));
$$ = new_node("enumitem");
Setattr($$,"name",$1);
@@ -6263,6 +6265,20 @@ opt_virtual : VIRTUAL
| empty
;
+virt_specifier_seq : OVERRIDE {
+ $$ = 0;
+ }
+ | FINAL {
+ $$ = 0;
+ }
+ | FINAL OVERRIDE {
+ $$ = 0;
+ }
+ | OVERRIDE FINAL {
+ $$ = 0;
+ }
+ ;
+
exception_specification : THROW LPAREN parms RPAREN {
$$.throws = $3;
$$.throwf = NewString("1");
@@ -6273,7 +6289,16 @@ exception_specification : THROW LPAREN parms RPAREN {
$$.throwf = 0;
$$.nexcept = NewString("true");
}
-
+ | virt_specifier_seq {
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ }
+ | NOEXCEPT virt_specifier_seq {
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = NewString("true");
+ }
| NOEXCEPT LPAREN expr RPAREN {
$$.throws = 0;
$$.throwf = 0;
@@ -6390,7 +6415,13 @@ template_decl : LESSTHAN valparms GREATERTHAN {
| empty { $$ = (char*)""; }
;
-idstring : ID { $$ = $1; }
+/* Identifiers including the C++11 identifiers with special meaning */
+identifier : ID { $$ = $1; }
+ | OVERRIDE { $$ = Swig_copy_string("override"); }
+ | FINAL { $$ = Swig_copy_string("final"); }
+ ;
+
+idstring : identifier { $$ = $1; }
| default_delete { $$ = $1.val; }
| string { $$ = $1; }
;
@@ -6442,7 +6473,7 @@ idcolontail : DCOLON idtemplate idcolontail {
;
-idtemplate : ID template_decl {
+idtemplate : identifier template_decl {
$$ = NewStringf("%s%s",$1,$2);
/* if (Len($2)) {
scanner_last_id(1);
@@ -6451,19 +6482,19 @@ idtemplate : ID template_decl {
;
/* Identifier, but no templates */
-idcolonnt : ID idcolontailnt {
+idcolonnt : identifier idcolontailnt {
$$ = 0;
if (!$$) $$ = NewStringf("%s%s", $1,$2);
Delete($2);
}
- | NONID DCOLON ID idcolontailnt {
+ | NONID DCOLON identifier idcolontailnt {
$$ = NewStringf("::%s%s",$3,$4);
Delete($4);
}
- | ID {
+ | identifier {
$$ = NewString($1);
}
- | NONID DCOLON ID {
+ | NONID DCOLON identifier {
$$ = NewStringf("::%s",$3);
}
| OPERATOR {
@@ -6474,17 +6505,17 @@ idcolonnt : ID idcolontailnt {
}
;
-idcolontailnt : DCOLON ID idcolontailnt {
+idcolontailnt : DCOLON identifier idcolontailnt {
$$ = NewStringf("::%s%s",$2,$3);
Delete($3);
}
- | DCOLON ID {
+ | DCOLON identifier {
$$ = NewStringf("::%s",$2);
}
| DCOLON OPERATOR {
$$ = NewStringf("::%s",$2);
}
- | DCNOT ID {
+ | DCNOT identifier {
$$ = NewStringf("::~%s",$2);
}
;