Allow calling of C++ methods from python

Message ID 20231215151938.3887-1-ssbssa@yahoo.de
State New
Headers
Series Allow calling of C++ methods from python |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Testing passed

Commit Message

Hannes Domani Dec. 15, 2023, 3:19 p.m. UTC
  Currently it's not possible to call C++ methods from python.
Using this example:
```
class B
{
  static int static_func ();
  int arg0_func ();
  int arg1_func (int arg1);
  int arg2_func (int arg1, int arg2);
};

B *b_obj = new B;
```

Trying to call B::static_func gives this error:
```
(gdb) py b_obj = gdb.parse_and_eval('b_obj')
(gdb) py print(b_obj['static_func']())
Traceback (most recent call last):
  File "<string>", line 1, in <module>
RuntimeError: Value is not callable (not TYPE_CODE_FUNC).
Error while executing Python code.
```

TYPE_CODE_METHOD was simply missing as a possible type in
valpy_call, now the same is possible:
```
(gdb) py b_obj = gdb.parse_and_eval('b_obj')
(gdb) py print(b_obj['static_func']())
1111
```

Note that it's necessary to explicitely add the this pointer
as the first argument in a call of non-static methods:
```
(gdb) py print(b_obj['arg0_func']())
Traceback (most recent call last):
  File "<string>", line 1, in <module>
gdb.error: Too few arguments in function call.
Error while executing Python code.
(gdb) py print(b_obj['arg0_func'](b_obj))
198
```

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=13326
---
 gdb/python/py-value.c                    |  5 +++--
 gdb/testsuite/gdb.python/py-value-cc.cc  | 25 ++++++++++++++++++++++++
 gdb/testsuite/gdb.python/py-value-cc.exp | 12 ++++++++++++
 3 files changed, 40 insertions(+), 2 deletions(-)
  

Comments

Tom Tromey Dec. 16, 2023, 12:30 a.m. UTC | #1
>>>>> "Hannes" == Hannes Domani <ssbssa@yahoo.de> writes:

Hannes>   static int static_func ();

Hannes> (gdb) py b_obj = gdb.parse_and_eval('b_obj')
Hannes> (gdb) py print(b_obj['static_func']())
Hannes> Traceback (most recent call last):
Hannes>   File "<string>", line 1, in <module>
Hannes> RuntimeError: Value is not callable (not TYPE_CODE_FUNC).

I'm a bit surprised that a static method is not TYPE_CODE_FUNC.
It seems like it should be, because these are really just functions.

Hannes> (gdb) py print(b_obj['arg0_func']())
Hannes> Traceback (most recent call last):
Hannes>   File "<string>", line 1, in <module>
Hannes> gdb.error: Too few arguments in function call.
Hannes> Error while executing Python code.
Hannes> (gdb) py print(b_obj['arg0_func'](b_obj))
Hannes> 198

How does this interact with overloading?

It seems to me that either b_obj['name'] has to return some kind of
overload set, or we need a different API, like b_obj.call_method('name', ...).

In the latter case we could have it automatically supply 'this', at
least when it's needed.

Tom
  
Hannes Domani Dec. 16, 2023, 11:40 a.m. UTC | #2
Am Samstag, 16. Dezember 2023, 01:30:31 MEZ hat Tom Tromey <tom@tromey.com> Folgendes geschrieben:

> >>>>> "Hannes" == Hannes Domani <ssbssa@yahoo.de> writes:
>
> Hannes>  static int static_func ();
>
> Hannes> (gdb) py b_obj = gdb.parse_and_eval('b_obj')
> Hannes> (gdb) py print(b_obj['static_func']())
> Hannes> Traceback (most recent call last):
> Hannes>  File "<string>", line 1, in <module>
> Hannes> RuntimeError: Value is not callable (not TYPE_CODE_FUNC).
>
> I'm a bit surprised that a static method is not TYPE_CODE_FUNC.
> It seems like it should be, because these are really just functions.

I never really questioned it being TYPE_CODE_METHOD, I was just wondering
instead if there is a flag that tells us that it is a static method.


> Hannes> (gdb) py print(b_obj['arg0_func']())
> Hannes> Traceback (most recent call last):
> Hannes>  File "<string>", line 1, in <module>
> Hannes> gdb.error: Too few arguments in function call.
> Hannes> Error while executing Python code.
> Hannes> (gdb) py print(b_obj['arg0_func'](b_obj))
> Hannes> 198
>
> How does this interact with overloading?

Probably as you expected, it doesn't work, since the [] operator
of gdb.Value doesn't know what to do:

(gdb) py print(b_obj['overloaded_func'])
Traceback (most recent call last):
  File "<string>", line 1, in <module>
gdb.error: cannot resolve overloaded method `overloaded_func': no arguments supplied
Error while executing Python code.


> It seems to me that either b_obj['name'] has to return some kind of
> overload set, or we need a different API, like b_obj.call_method('name', ...).
>
> In the latter case we could have it automatically supply 'this', at
> least when it's needed.

The overload set would probably be relatively easy to implement, while
the call_method would be easier to use, if it automatically chooses the
right one based on the arguments.


Regards
Hannes
  
Hannes Domani Jan. 3, 2024, 10:38 a.m. UTC | #3
Ping.


Am Samstag, 16. Dezember 2023, 12:40:25 MEZ hat Hannes Domani <ssbssa@yahoo.de> Folgendes geschrieben:

> Am Samstag, 16. Dezember 2023, 01:30:31 MEZ hat Tom Tromey <tom@tromey.com> Folgendes geschrieben:
>
> > >>>>> "Hannes" == Hannes Domani <ssbssa@yahoo.de> writes:
> >
> > Hannes>  static int static_func ();
> >
> > Hannes> (gdb) py b_obj = gdb.parse_and_eval('b_obj')
> > Hannes> (gdb) py print(b_obj['static_func']())
> > Hannes> Traceback (most recent call last):
> > Hannes>  File "<string>", line 1, in <module>
> > Hannes> RuntimeError: Value is not callable (not TYPE_CODE_FUNC).
> >
> > I'm a bit surprised that a static method is not TYPE_CODE_FUNC.
> > It seems like it should be, because these are really just functions.
>
> I never really questioned it being TYPE_CODE_METHOD, I was just wondering
> instead if there is a flag that tells us that it is a static method.
>
>
> > Hannes> (gdb) py print(b_obj['arg0_func']())
> > Hannes> Traceback (most recent call last):
> > Hannes>  File "<string>", line 1, in <module>
> > Hannes> gdb.error: Too few arguments in function call.
> > Hannes> Error while executing Python code.
> > Hannes> (gdb) py print(b_obj['arg0_func'](b_obj))
> > Hannes> 198
> >
> > How does this interact with overloading?
>
> Probably as you expected, it doesn't work, since the [] operator
> of gdb.Value doesn't know what to do:
>
> (gdb) py print(b_obj['overloaded_func'])
> Traceback (most recent call last):
>   File "<string>", line 1, in <module>
> gdb.error: cannot resolve overloaded method `overloaded_func': no arguments supplied
> Error while executing Python code.
>
>
> > It seems to me that either b_obj['name'] has to return some kind of
> > overload set, or we need a different API, like b_obj.call_method('name', ...).
> >
> > In the latter case we could have it automatically supply 'this', at
> > least when it's needed.
>
> The overload set would probably be relatively easy to implement, while
> the call_method would be easier to use, if it automatically chooses the
> right one based on the arguments.
>
>
> Regards
>
> Hannes
  
Hannes Domani Jan. 17, 2024, 3:52 p.m. UTC | #4
Ping.


Am Mittwoch, 3. Januar 2024, 11:38:57 MEZ hat Hannes Domani <ssbssa@yahoo.de> Folgendes geschrieben:

> Ping.
>
>
> Am Samstag, 16. Dezember 2023, 12:40:25 MEZ hat Hannes Domani <ssbssa@yahoo.de> Folgendes geschrieben:
>
> > Am Samstag, 16. Dezember 2023, 01:30:31 MEZ hat Tom Tromey <tom@tromey.com> Folgendes geschrieben:
> >
> > > >>>>> "Hannes" == Hannes Domani <ssbssa@yahoo.de> writes:
> > >
> > > Hannes>  static int static_func ();
> > >
> > > Hannes> (gdb) py b_obj = gdb.parse_and_eval('b_obj')
> > > Hannes> (gdb) py print(b_obj['static_func']())
> > > Hannes> Traceback (most recent call last):
> > > Hannes>  File "<string>", line 1, in <module>
> > > Hannes> RuntimeError: Value is not callable (not TYPE_CODE_FUNC).
> > >
> > > I'm a bit surprised that a static method is not TYPE_CODE_FUNC.
> > > It seems like it should be, because these are really just functions.
> >
> > I never really questioned it being TYPE_CODE_METHOD, I was just wondering
> > instead if there is a flag that tells us that it is a static method.
> >
> >
> > > Hannes> (gdb) py print(b_obj['arg0_func']())
> > > Hannes> Traceback (most recent call last):
> > > Hannes>  File "<string>", line 1, in <module>
> > > Hannes> gdb.error: Too few arguments in function call.
> > > Hannes> Error while executing Python code.
> > > Hannes> (gdb) py print(b_obj['arg0_func'](b_obj))
> > > Hannes> 198
> > >
> > > How does this interact with overloading?
> >
> > Probably as you expected, it doesn't work, since the [] operator
> > of gdb.Value doesn't know what to do:
> >
> > (gdb) py print(b_obj['overloaded_func'])
> > Traceback (most recent call last):
> >   File "<string>", line 1, in <module>
> > gdb.error: cannot resolve overloaded method `overloaded_func': no arguments supplied
> > Error while executing Python code.
> >
> >
> > > It seems to me that either b_obj['name'] has to return some kind of
> > > overload set, or we need a different API, like b_obj.call_method('name', ...).
> > >
> > > In the latter case we could have it automatically supply 'this', at
> > > least when it's needed.
> >
> > The overload set would probably be relatively easy to implement, while
> > the call_method would be easier to use, if it automatically chooses the
> > right one based on the arguments.
> >
> >
> > Regards
> >
> > Hannes
  
Hannes Domani Jan. 26, 2024, 5:02 p.m. UTC | #5
Ping.


Am Mittwoch, 17. Januar 2024, 16:52:56 MEZ hat Hannes Domani <ssbssa@yahoo.de> Folgendes geschrieben:

> Ping.
>
>
> Am Mittwoch, 3. Januar 2024, 11:38:57 MEZ hat Hannes Domani <ssbssa@yahoo.de> Folgendes geschrieben:
>
> > Ping.
> >
> >
> > Am Samstag, 16. Dezember 2023, 12:40:25 MEZ hat Hannes Domani <ssbssa@yahoo.de> Folgendes geschrieben:
> >
> > > Am Samstag, 16. Dezember 2023, 01:30:31 MEZ hat Tom Tromey <tom@tromey.com> Folgendes geschrieben:
> > >
> > > > >>>>> "Hannes" == Hannes Domani <ssbssa@yahoo.de> writes:
> > > >
> > > > Hannes>  static int static_func ();
> > > >
> > > > Hannes> (gdb) py b_obj = gdb.parse_and_eval('b_obj')
> > > > Hannes> (gdb) py print(b_obj['static_func']())
> > > > Hannes> Traceback (most recent call last):
> > > > Hannes>  File "<string>", line 1, in <module>
> > > > Hannes> RuntimeError: Value is not callable (not TYPE_CODE_FUNC).
> > > >
> > > > I'm a bit surprised that a static method is not TYPE_CODE_FUNC.
> > > > It seems like it should be, because these are really just functions.
> > >
> > > I never really questioned it being TYPE_CODE_METHOD, I was just wondering
> > > instead if there is a flag that tells us that it is a static method.
> > >
> > >
> > > > Hannes> (gdb) py print(b_obj['arg0_func']())
> > > > Hannes> Traceback (most recent call last):
> > > > Hannes>  File "<string>", line 1, in <module>
> > > > Hannes> gdb.error: Too few arguments in function call.
> > > > Hannes> Error while executing Python code.
> > > > Hannes> (gdb) py print(b_obj['arg0_func'](b_obj))
> > > > Hannes> 198
> > > >
> > > > How does this interact with overloading?
> > >
> > > Probably as you expected, it doesn't work, since the [] operator
> > > of gdb.Value doesn't know what to do:
> > >
> > > (gdb) py print(b_obj['overloaded_func'])
> > > Traceback (most recent call last):
> > >   File "<string>", line 1, in <module>
> > > gdb.error: cannot resolve overloaded method `overloaded_func': no arguments supplied
> > > Error while executing Python code.
> > >
> > >
> > > > It seems to me that either b_obj['name'] has to return some kind of
> > > > overload set, or we need a different API, like b_obj.call_method('name', ...).
> > > >
> > > > In the latter case we could have it automatically supply 'this', at
> > > > least when it's needed.
> > >
> > > The overload set would probably be relatively easy to implement, while
> > > the call_method would be easier to use, if it automatically chooses the
> > > right one based on the arguments.
> > >
> > >
> > > Regards
> > >
> > > Hannes
  
Tom Tromey Feb. 8, 2024, 4:29 p.m. UTC | #6
>>>>> "Hannes" == Hannes Domani <ssbssa@yahoo.de> writes:

Hannes> Currently it's not possible to call C++ methods from python.

Hannes> Note that it's necessary to explicitely add the this pointer
Hannes> as the first argument in a call of non-static methods:

While I think it would be good to handle overloading, it occurs to me
that we don't really try to do this for global calls either.  And, the
approach you've taken here does not prevent future improvements.

So, I think this is ok.  Thank you.

Approved-By: Tom Tromey <tom@tromey.com>

Tom
  
Hannes Domani Feb. 8, 2024, 7:11 p.m. UTC | #7
Am Donnerstag, 8. Februar 2024 um 17:29:11 MEZ hat Tom Tromey <tom@tromey.com> Folgendes geschrieben:

> >>>>> "Hannes" == Hannes Domani <ssbssa@yahoo.de> writes:
>
> Hannes> Currently it's not possible to call C++ methods from python.
>
> Hannes> Note that it's necessary to explicitely add the this pointer
> Hannes> as the first argument in a call of non-static methods:
>
> While I think it would be good to handle overloading, it occurs to me
> that we don't really try to do this for global calls either.  And, the
> approach you've taken here does not prevent future improvements.
>
> So, I think this is ok.  Thank you.
>
> Approved-By: Tom Tromey <tom@tromey.com>

Pushed, thanks.


Hannes
  

Patch

diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index f360f849ce6..f5e22593254 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -1214,10 +1214,11 @@  valpy_call (PyObject *self, PyObject *args, PyObject *keywords)
       GDB_PY_HANDLE_EXCEPTION (except);
     }
 
-  if (ftype->code () != TYPE_CODE_FUNC)
+  if (ftype->code () != TYPE_CODE_FUNC && ftype->code () != TYPE_CODE_METHOD)
     {
       PyErr_SetString (PyExc_RuntimeError,
-		       _("Value is not callable (not TYPE_CODE_FUNC)."));
+		       _("Value is not callable (not TYPE_CODE_FUNC"
+			 " or TYPE_CODE_METHOD)."));
       return NULL;
     }
 
diff --git a/gdb/testsuite/gdb.python/py-value-cc.cc b/gdb/testsuite/gdb.python/py-value-cc.cc
index d82e385d6c5..61a8f1a7b0f 100644
--- a/gdb/testsuite/gdb.python/py-value-cc.cc
+++ b/gdb/testsuite/gdb.python/py-value-cc.cc
@@ -37,8 +37,33 @@  union U {
 class B : public A {
  public:
   char a;
+
+  static int static_func ();
+  int arg0_func ();
+  int arg1_func (int arg1);
+  int arg2_func (int arg1, int arg2);
 };
 
+int B::static_func ()
+{
+  return 1111;
+}
+
+int B::arg0_func ()
+{
+  return A::a + a;
+}
+
+int B::arg1_func (int arg1)
+{
+  return a * arg1;
+}
+
+int B::arg2_func (int arg1, int arg2)
+{
+  return a * arg1 + arg2;
+}
+
 struct X
 {
   union { int x; char y; };
diff --git a/gdb/testsuite/gdb.python/py-value-cc.exp b/gdb/testsuite/gdb.python/py-value-cc.exp
index b6571cd8297..6a424e3b407 100644
--- a/gdb/testsuite/gdb.python/py-value-cc.exp
+++ b/gdb/testsuite/gdb.python/py-value-cc.exp
@@ -99,3 +99,15 @@  gdb_test "python print(uu\[uu_fields\[1\]\]\['a'\])" "1000" "uu.a via field"
 # Test overloaded operators.
 gdb_test_no_output "python a = gdb.parse_and_eval('a')" "init a"
 gdb_test "python print(a + 5)" "10" "a + 5"
+
+# Test inferior function calls of methods.
+gdb_test "py print(b_obj\['static_func'\]())" "1111"
+gdb_test "py print(b_obj\['arg0_func'\]())" ".*Too few arguments in function call.*"
+gdb_test "py print(b_obj\['arg0_func'\](b_obj))" "198"
+gdb_test "py print(b_obj\['arg1_func'\]())" ".*Too few arguments in function call.*"
+gdb_test "py print(b_obj\['arg1_func'\](b_obj))" ".*Too few arguments in function call.*"
+gdb_test "py print(b_obj\['arg1_func'\](b_obj, 3))" "294"
+gdb_test "py print(b_obj\['arg2_func'\]())" ".*Too few arguments in function call.*"
+gdb_test "py print(b_obj\['arg2_func'\](b_obj))" ".*Too few arguments in function call.*"
+gdb_test "py print(b_obj\['arg2_func'\](b_obj, 4))" ".*Too few arguments in function call.*"
+gdb_test "py print(b_obj\['arg2_func'\](b_obj, 5, 6))" "496"