Functions
#########

Before proceeding with this section, make sure that you are already familiar
with the basics of binding functions and classes, as explained in :doc:`/basics`
and :doc:`/classes`. The following guide is applicable to both free and member
functions, i.e. *methods* in Python.

.. _return_value_policies:

Return value policies
=====================

Python and SystemVerilog use fundamentally different ways of managing the memory and
lifetime of objects managed by them. This can lead to issues when creating
bindings for functions that return a non-trivial type. When non-trivial type returned
SystemVerilog should manage the object life time and deallocate the memory occupied by the object with 
:func:`py_object::delete` when the object is no longer needed.

Return value policies are tricky, and it's very important to get them right.
Just to illustrate what can go wrong, consider the following simple example:

.. code-block:: systemverilog 

    //equivalent to: from decimal import Decimal 
    py_object Decimal = py_module::import_("decimal").attr("Decimal").obtain();
    py_object decimal_exp = Decimal.attr("exp");

    // Compute the e^n for n=0..4
    for (int n = 0; n < 5; n++) begin
        py_object d = Decimal.call(py::int_(n));
        py::print(py::str_("Result: "), decimal_exp.call(d));
    end

What's going on here? When ``decimal_exp.call(d)`` is called and the result object 
passed to ``py::print()`` function,  the handler the result of ``decimal_exp.call(d)``
no longer available. Therefore it is not possible to access the result of ``decimal_exp.call(d)``
and deallocate the memory occupied by the object. The same issue occurs for the object ``d``.
The the ``d.delete()`` function not called to deallocate the memory occupied by the object during
the loop. Non trivial type objects should be deallocated by calling the :func:`py_object::delete` function.
The Python garbage collector will not deallocate the memory occupied by the this objects since the object 
reference count is not zero.

The proper way to write to the above code is to deallocate the memory occupied by the object
``d`` and the result of ``decimal_exp.call(d)``. 

.. code-block:: systemverilog

    //equivalent to: from decimal import Decimal 
    py_object Decimal = py_module::import_("decimal").attr("Decimal").obtain();
    py_object decimal_exp = Decimal.attr("exp");

    // Compute the e^n for n=0..4
    for (int n = 0; n < 5; n++) begin
        py_object d = Decimal.call(py::int_(n));
        py_object result = decimal_exp.call(d);
        py::print(py::str_("Result: "), result);
        result.delete();
        d.delete();
    end


.. _python_objects_as_args:

Python objects as arguments
===========================

PyStim exposes all major Python types using thin SystemVerilog wrapper classes. These
wrapper classes can also be used as parameters of functions in bindings, which
makes it possible to directly work with native Python types on the SystemVerilog side.
For instance, the following statement iterates over a Python ``dict``:

.. code-block:: systemverilog 

    function void print_dict(py::py_dict dict);
        /* Easily interact with Python types */
        iterator it = students_dict.get_iterator();
        while(it.has_next())begin
            py_pair p = it.next().cast_dict_pair();
            py_object key = p.get_key();
            py_object value = p.get_value();
            $display("Key: %s, Value: %s", key.cast_string().get_value(),
                    value.cast_uint().get_value());

        end
    endfunction

.. note::
    Currently, the SystemVerilog ``dict`` wrapper class supports only keys of type ``string``. 

For more information on using Python objects in SystemVerilog, see :doc:`/advanced/pysv/index`.

Accepting \*args and \*\*kwargs
===============================

Python provides a useful mechanism to define functions that accept arbitrary
numbers of arguments and keyword arguments:

.. code-block:: python

   def generic(*args, **kwargs):
       ...  # do something with args and kwargs

To call the Python function with args and kwargs, use SystemVerilog named arguments mapping:

.. code-block:: systemverilog

    py_tuple args = ...;
    py_dict kwargs = ...;
    ... // do something with args and kwargs
    some_attribute.call(.args(args), .kwargs(kwargs));



The class ``py_args`` derives from ``py_tuple`` and ``py_kwargs`` derives
from ``py_dict``.

You may also use just one or the other, and may combine these with other
arguments.  Note, however, that ``args`` and ``kwargs`` must always be named mapped arguments.

A demonstration of ``args`` and ``kwargs`` is available in ``examples/misc/test_args_and_kwargs.sv``.


