From 5aff26fcb51330e82e9c945bd30dbbe09076e1ee Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Fri, 3 Mar 2017 19:37:28 +0000 Subject: [PATCH] Add support for parsing and wrapping member const function pointers --- Examples/test-suite/common.mk | 1 + .../java/member_pointer_const_runme.java | 58 ++++++++ Examples/test-suite/member_pointer_const.i | 135 ++++++++++++++++++ .../python/member_pointer_const_runme.py | 48 +++++++ Source/CParse/parser.y | 94 ++++++++++-- Source/Swig/stype.c | 68 +++++++-- 6 files changed, 383 insertions(+), 21 deletions(-) create mode 100644 Examples/test-suite/java/member_pointer_const_runme.java create mode 100644 Examples/test-suite/member_pointer_const.i create mode 100644 Examples/test-suite/python/member_pointer_const_runme.py diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index 39d4f1fa6..574479d1b 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -278,6 +278,7 @@ CPP_TEST_CASES += \ memberin_extend \ member_funcptr_galore \ member_pointer \ + member_pointer_const \ member_template \ minherit \ minherit2 \ diff --git a/Examples/test-suite/java/member_pointer_const_runme.java b/Examples/test-suite/java/member_pointer_const_runme.java new file mode 100644 index 000000000..06e0bbcb3 --- /dev/null +++ b/Examples/test-suite/java/member_pointer_const_runme.java @@ -0,0 +1,58 @@ +import member_pointer_const.*; + +public class member_pointer_const_runme { + + static { + try { + System.loadLibrary("member_pointer_const"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e); + System.exit(1); + } + } + + public static SWIGTYPE_m_Shape__f_void__double memberPtr = null; + + public static void main(String argv[]) { + // Get the pointers + + SWIGTYPE_m_Shape__f_void__double area_pt = member_pointer_const.areapt(); + SWIGTYPE_m_Shape__f_void__double perim_pt = member_pointer_const.perimeterpt(); + + // Create some objects + + Square s = new Square(10); + + // Do some calculations + + check( "Square area ", 100.0, member_pointer_const.do_op(s,area_pt) ); + check( "Square perim", 40.0, member_pointer_const.do_op(s,perim_pt) ); + + memberPtr = member_pointer_const.getAreavar(); + memberPtr = member_pointer_const.getPerimetervar(); + + // Try the variables + check( "Square area ", 100.0, member_pointer_const.do_op(s,member_pointer_const.getAreavar()) ); + check( "Square perim", 40.0, member_pointer_const.do_op(s,member_pointer_const.getPerimetervar()) ); + + // Modify one of the variables + member_pointer_const.setAreavar(perim_pt); + + check( "Square perimeter", 40.0, member_pointer_const.do_op(s,member_pointer_const.getAreavar()) ); + + // Try the constants + + memberPtr = member_pointer_const.AREAPT; + memberPtr = member_pointer_const.PERIMPT; + memberPtr = member_pointer_const.NULLPT; + + check( "Square area ", 100.0, member_pointer_const.do_op(s,member_pointer_const.AREAPT) ); + check( "Square perim", 40.0, member_pointer_const.do_op(s,member_pointer_const.PERIMPT) ); + + } + + private static void check(String what, double expected, double actual) { + if (expected != actual) + throw new RuntimeException("Failed: " + what + " Expected: " + expected + " Actual: " + actual); + } +} diff --git a/Examples/test-suite/member_pointer_const.i b/Examples/test-suite/member_pointer_const.i new file mode 100644 index 000000000..39caa3bad --- /dev/null +++ b/Examples/test-suite/member_pointer_const.i @@ -0,0 +1,135 @@ +%module member_pointer_const +// Same as member_pointer.i but using member pointer const functions + +%{ +#if defined(__SUNPRO_CC) +#pragma error_messages (off, badargtype2w) /* Formal argument ... is being passed extern "C" ... */ +#pragma error_messages (off, wbadinit) /* Using extern "C" ... to initialize ... */ +#pragma error_messages (off, wbadasg) /* Assigning extern "C" ... */ +#endif +%} + +%inline %{ +class Shape { +public: + Shape() { + nshapes++; + } + virtual ~Shape() { + nshapes--; + }; + double x, y; + double *z; + + void move(double dx, double dy); + virtual double area(void) const = 0; + virtual double perimeter(void) const = 0; + static int nshapes; +}; + +class Circle : public Shape { +private: + double radius; +public: + Circle(double r) : radius(r) { }; + virtual double area(void) const; + virtual double perimeter(void) const; +}; + +class Square : public Shape { +private: + double width; +public: + Square(double w) : width(w) { }; + virtual double area(void) const; + virtual double perimeter(void) const; +}; + +extern double do_op(Shape *s, double (Shape::*m)(void) const); + +/* Functions that return member pointers */ + +extern double (Shape::*areapt())(void) const; +extern double (Shape::*perimeterpt())(void) const; + +/* Global variables that are member pointers */ +extern double (Shape::*areavar)(void) const; +extern double (Shape::*perimetervar)(void) const; + +%} + +%{ +# define SWIG_M_PI 3.14159265358979323846 + +/* Move the shape to a new location */ +void Shape::move(double dx, double dy) { + x += dx; + y += dy; +} + +int Shape::nshapes = 0; + +double Circle::area(void) const { + return SWIG_M_PI*radius*radius; +} + +double Circle::perimeter(void) const { + return 2*SWIG_M_PI*radius; +} + +double Square::area(void) const { + return width*width; +} + +double Square::perimeter(void) const { + return 4*width; +} + +double do_op(Shape *s, double (Shape::*m)(void) const) { + return (s->*m)(); +} + +double (Shape::*areapt())(void) const { + return &Shape::area; +} + +double (Shape::*perimeterpt())(void) const { + return &Shape::perimeter; +} + +/* Member pointer variables */ +double (Shape::*areavar)(void) const = &Shape::area; +double (Shape::*perimetervar)(void) const = &Shape::perimeter; +%} + + +/* Some constants */ +%constant double (Shape::*AREAPT)(void) const = &Shape::area; +%constant double (Shape::*PERIMPT)(void) const = &Shape::perimeter; +%constant double (Shape::*NULLPT)(void) const = 0; + +/* +%inline %{ + struct Funktions { + void retByRef(int & (*d)(double)) {} + }; + void byRef(int & (Funktions::*d)(double)) {} +%} +*/ + +%inline %{ + +struct Funktions { + int addByValue(const int &a, int b) const { return a+b; } + int * addByPointer(const int &a, int b) const { static int val; val = a+b; return &val; } + int & addByReference(const int &a, int b) const { static int val; val = a+b; return val; } +}; + +int call1(int (Funktions::*d)(const int &, int) const, int a, int b) { Funktions f; return (f.*d)(a, b); } +//int call2(int * (Funktions::*d)(const int &, int) const, int a, int b) { Funktions f; return *(f.*d)(a, b); } +//int call3(int & (Funktions::*d)(const int &, int) const, int a, int b) { Funktions f; return (f.*d)(a, b); } +%} + +%constant int (Funktions::*ADD_BY_VALUE)(const int &, int) const = &Funktions::addByValue; +//%constant int * (Funktions::*ADD_BY_POINTER)(const int &, int) const = &Funktions::addByPointer; +//%constant int & (Funktions::*ADD_BY_REFERENCE)(const int &, int) const = &Funktions::addByReference; diff --git a/Examples/test-suite/python/member_pointer_const_runme.py b/Examples/test-suite/python/member_pointer_const_runme.py new file mode 100644 index 000000000..0e56c1574 --- /dev/null +++ b/Examples/test-suite/python/member_pointer_const_runme.py @@ -0,0 +1,48 @@ +# Example using pointers to member functions + +from member_pointer_const import * + + +def check(what, expected, actual): + if expected != actual: + raise RuntimeError( + "Failed: ", what, " Expected: ", expected, " Actual: ", actual) + +# Get the pointers + +area_pt = areapt() +perim_pt = perimeterpt() + +# Create some objects + +s = Square(10) + +# Do some calculations + +check("Square area ", 100.0, do_op(s, area_pt)) +check("Square perim", 40.0, do_op(s, perim_pt)) + +memberPtr = cvar.areavar +memberPtr = cvar.perimetervar + +# Try the variables +check("Square area ", 100.0, do_op(s, cvar.areavar)) +check("Square perim", 40.0, do_op(s, cvar.perimetervar)) + +# Modify one of the variables +cvar.areavar = perim_pt + +check("Square perimeter", 40.0, do_op(s, cvar.areavar)) + +# Try the constants + +memberPtr = AREAPT +memberPtr = PERIMPT +memberPtr = NULLPT + +check("Square area ", 100.0, do_op(s, AREAPT)) +check("Square perim", 40.0, do_op(s, PERIMPT)) + +check("Add by value", 3, call1(ADD_BY_VALUE, 1, 2)) +#check("Add by pointer", 7, call2(ADD_BY_POINTER, 3, 4)) +#check("Add by reference", 11, call3(ADD_BY_REFERENCE, 5, 6)) diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index 37cc4d0bd..e7905e68f 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -1351,6 +1351,37 @@ static void mark_nodes_as_extend(Node *n) { } } +/* ----------------------------------------------------------------------------- + * add_qualifier_to_declarator + * + * Adding a qualifier to a pointer to member function is a special case. + * For example : typedef double (Cls::*pmf)(void) const; + * The declarator is : m(Cls).f(void). + * We need : m(Cls).q(const).f(void). + * ----------------------------------------------------------------------------- */ + +static String *add_qualifier_to_declarator(SwigType *type, SwigType *qualifier) { + int is_pointer_to_member_function = 0; + String *decl = Copy(type); + assert(qualifier); + if (SwigType_ismemberpointer(decl)) { + String *memberptr = SwigType_pop(decl); + if (SwigType_isfunction(decl)) { + assert(!SwigType_isqualifier(decl)); + SwigType_push(decl, qualifier); + SwigType_push(decl, memberptr); + is_pointer_to_member_function = 1; + } else { + Delete(decl); + decl = Copy(type); + } + Delete(memberptr); + } + if (!is_pointer_to_member_function) + SwigType_push(decl, qualifier); + return decl; +} + %} %union { @@ -1751,7 +1782,6 @@ constant_directive : CONSTANT identifier EQUAL definetype SEMI { } } - | CONSTANT type declarator def_args SEMI { if (($4.type != T_ERROR) && ($4.type != T_SYMBOL)) { SwigType_push($2,$3.type); @@ -1768,12 +1798,38 @@ constant_directive : CONSTANT identifier EQUAL definetype SEMI { SetFlag($$,"feature:immutable"); add_symbols($$); } else { - if ($4.type == T_ERROR) { - Swig_warning(WARN_PARSE_UNSUPPORTED_VALUE,cparse_file,cparse_line,"Unsupported constant value\n"); - } + if ($4.type == T_ERROR) { + Swig_warning(WARN_PARSE_UNSUPPORTED_VALUE,cparse_file,cparse_line, "Unsupported constant value\n"); + } $$ = 0; } } + /* Member const function pointers . eg. + %constant short (Funcs::*pmf)(bool) const = &Funcs::F; */ + | CONSTANT type direct_declarator LPAREN parms RPAREN CONST_QUAL def_args SEMI { + if (($8.type != T_ERROR) && ($8.type != T_SYMBOL)) { + SwigType_add_function($2, $5); + SwigType_add_qualifier($2, "const"); + SwigType_push($2, $3.type); + /* Sneaky callback function trick */ + if (SwigType_isfunction($2)) { + SwigType_add_pointer($2); + } + $$ = new_node("constant"); + Setattr($$, "name", $3.id); + Setattr($$, "type", $2); + Setattr($$, "value", $8.val); + if ($8.rawval) Setattr($$, "rawval", $8.rawval); + Setattr($$, "storage", "%constant"); + SetFlag($$, "feature:immutable"); + add_symbols($$); + } else { + if ($8.type == T_ERROR) { + Swig_warning(WARN_PARSE_UNSUPPORTED_VALUE,cparse_file,cparse_line, "Unsupported constant value\n"); + } + $$ = 0; + } + } | CONSTANT error SEMI { Swig_warning(WARN_PARSE_BAD_VALUE,cparse_file,cparse_line,"Bad constant value (ignored).\n"); $$ = 0; @@ -2942,12 +2998,14 @@ c_declaration : c_decl { ------------------------------------------------------------ */ c_decl : storage_class type declarator initializer c_decl_tail { + String *decl = $3.type; $$ = new_node("cdecl"); - if ($4.qualifier) SwigType_push($3.type,$4.qualifier); + if ($4.qualifier) + decl = add_qualifier_to_declarator($3.type, $4.qualifier); Setattr($$,"type",$2); Setattr($$,"storage",$1); Setattr($$,"name",$3.id); - Setattr($$,"decl",$3.type); + Setattr($$,"decl",decl); Setattr($$,"parms",$3.parms); Setattr($$,"value",$4.val); Setattr($$,"throws",$4.throws); @@ -4959,6 +5017,27 @@ parameter_declarator : declarator def_args { $$.id = 0; $$.defarg = $1.rawval ? $1.rawval : $1.val; } + /* Member const function pointer parameters. eg. + int f(short (Funcs::*parm)(bool) const); */ + | direct_declarator LPAREN parms RPAREN CONST_QUAL { + SwigType *t; + $$ = $1; + t = NewStringEmpty(); + SwigType_add_function(t,$3); + SwigType_add_qualifier(t, "const"); + if (!$$.have_parms) { + $$.parms = $3; + $$.have_parms = 1; + } + if (!$$.type) { + $$.type = t; + } else { + SwigType_push(t, $$.type); + Delete($$.type); + $$.type = t; + } + $$.defarg = 0; + } ; plain_declarator : declarator { @@ -5002,7 +5081,6 @@ plain_declarator : declarator { } ; - declarator : pointer notso_direct_declarator { $$ = $2; if ($$.type) { @@ -5404,7 +5482,7 @@ direct_declarator : idcolon { Delete($$.type); $$.type = t; } - } + } /* User-defined string literals. eg. int operator"" _mySuffix(const char* val, int length) {...} */ /* This produces one S/R conflict. */ diff --git a/Source/Swig/stype.c b/Source/Swig/stype.c index a57222745..7cfb126ab 100644 --- a/Source/Swig/stype.c +++ b/Source/Swig/stype.c @@ -45,7 +45,7 @@ * 'a(n).' = Array of size n [n] * 'f(..,..).' = Function with arguments (args) * 'q(str).' = Qualifier (such as const or volatile) (const, volatile) - * 'm(qual).' = Pointer to member (qual::*) + * 'm(cls).' = Pointer to member (cls::*) * * The encoding follows the order that you might describe a type in words. * For example "p.a(200).int" is "A pointer to array of int's" and @@ -62,6 +62,13 @@ * * Replace(t,"q(const).","",DOH_REPLACE_ANY) * + * More examples: + * + * String Encoding C Example + * --------------- --------- + * p.f(bool).q(const).long const long (*)(bool) + * m(Funcs).q(const).f(bool).long long (Funcs::*)(bool) const + * * For the most part, this module tries to minimize the use of special * characters (*, [, <, etc...) in its type encoding. One reason for this * is that SWIG might be extended to encode data in formats such as XML @@ -372,7 +379,7 @@ SwigType *SwigType_default_create(const SwigType *ty) { * and is very close to the type deduction used in partial template class * specialization matching in that the most specialized type is always chosen. * SWIGTYPE is used as the generic type. The basic idea is to repeatedly call - * this function to find a deduced type unless until nothing matches. + * this function to find a deduced type until nothing matches. * * The type t must have already been converted to the default type via a call to * SwigType_default_create() before calling this function. @@ -528,8 +535,10 @@ String *SwigType_str(const SwigType *s, const_String_or_char_ptr id) { String *element = 0; String *nextelement; String *forwardelement; + String *member_const_function_element = 0; List *elements; int nelements, i; + int member_const_function = 0; if (id) { /* stringify the id expanding templates, for example when the id is a fully qualified templated class name */ @@ -560,11 +569,15 @@ String *SwigType_str(const SwigType *s, const_String_or_char_ptr id) { forwardelement = 0; } if (SwigType_isqualifier(element)) { - DOH *q = 0; - q = SwigType_parm(element); - Insert(result, 0, " "); - Insert(result, 0, q); - Delete(q); + if (!member_const_function) { + DOH *q = 0; + q = SwigType_parm(element); + Insert(result, 0, " "); + Insert(result, 0, q); + Delete(q); + } else { + member_const_function = 0; + } } else if (SwigType_ispointer(element)) { Insert(result, 0, "*"); if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) { @@ -580,6 +593,10 @@ String *SwigType_str(const SwigType *s, const_String_or_char_ptr id) { Insert(result, 0, "("); Append(result, ")"); } + if (SwigType_isqualifier(nextelement)) { + member_const_function_element = nextelement; + member_const_function = 1; + } Delete(q); } else if (SwigType_isreference(element)) { Insert(result, 0, "&"); @@ -613,6 +630,13 @@ String *SwigType_str(const SwigType *s, const_String_or_char_ptr id) { Append(result, ","); } Append(result, ")"); + if (member_const_function_element) { + String *p = SwigType_str(member_const_function_element, 0); + Append(result, " "); + Append(result, p); + Delete(p); + member_const_function_element = 0; + } Delete(parms); } else { if (strcmp(Char(element), "v(...)") == 0) { @@ -763,6 +787,7 @@ String *SwigType_rcaststr(const SwigType *s, const_String_or_char_ptr name) { String *element = 0; String *nextelement; String *forwardelement; + String *member_const_function_element = 0; SwigType *td, *tc = 0; const SwigType *rs; List *elements; @@ -771,6 +796,7 @@ String *SwigType_rcaststr(const SwigType *s, const_String_or_char_ptr name) { int firstarray = 1; int isreference = 0; int isfunction = 0; + int member_const_function = 0; result = NewStringEmpty(); @@ -816,12 +842,16 @@ String *SwigType_rcaststr(const SwigType *s, const_String_or_char_ptr name) { forwardelement = 0; } if (SwigType_isqualifier(element)) { - DOH *q = 0; - q = SwigType_parm(element); - Insert(result, 0, " "); - Insert(result, 0, q); - Delete(q); - clear = 0; + if (!member_const_function) { + DOH *q = 0; + q = SwigType_parm(element); + Insert(result, 0, " "); + Insert(result, 0, q); + Delete(q); + clear = 0; + } else { + member_const_function = 0; + } } else if (SwigType_ispointer(element)) { Insert(result, 0, "*"); if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) { @@ -839,6 +869,10 @@ String *SwigType_rcaststr(const SwigType *s, const_String_or_char_ptr name) { Insert(result, 0, "("); Append(result, ")"); } + if (SwigType_isqualifier(nextelement)) { + member_const_function_element = nextelement; + member_const_function = 1; + } firstarray = 0; } else if (SwigType_isreference(element)) { Insert(result, 0, "&"); @@ -885,6 +919,14 @@ String *SwigType_rcaststr(const SwigType *s, const_String_or_char_ptr name) { } Append(result, ")"); Delete(parms); + if (member_const_function_element) { + String *p = SwigType_str(member_const_function_element, 0); + Append(result, " "); + Append(result, p); + Delete(p); + member_const_function_element = 0; + clear = 0; + } isfunction = 1; } else { String *bs = SwigType_namestr(element);