SIP: Virtual pure method with std::function

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

SIP: Virtual pure method with std::function

jcabieces

Hi,

I would like to define an interface (abstract class) where one method is
pure virtual and takes a std::function as a parameter.

I have this in my C++ header

```
class MyInterface
{
  public:

#ifndef SIP_RUN
    virtual MyObject *doThings( const QString &filePath, const QUrl &url,
                                const std::function<void ( const QString & )> &errorCallback = {} ) = 0 ;
#else
    virtual MyObject *doThings( const QString &filePath, const QUrl &url, SIP_PYCALLABLE / AllowNone / ) = 0;
    % MethodCode
    {
      Py_BEGIN_ALLOW_THREADS

      sipCpp->MyInterface::doThings( *a0, *a1, [a2]( const QString  & error )
      {
        SIP_BLOCK_THREADS
        Py_XDECREF( sipCallMethod( NULL, a2, "D", &error, sipType_QString, NULL ) );
        SIP_UNBLOCK_THREADS
      } );

      sipRes = sipCpp;

      Py_END_ALLOW_THREADS
    }
    % End
#endif
```

and this in the corresponding sip file

```
class MyInterface
{

%TypeHeaderCode
#include "myinterface.h"
%End
  public:

    virtual MyObject *doThings( const QString &filePath, const QUrl &url, SIP_PYCALLABLE / AllowNone / ) = 0;
%MethodCode
    {
      Py_BEGIN_ALLOW_THREADS

      sipCpp->MyInterface::doThings( *a0, *a1, [a2]( const QString  & error )
      {
        SIP_BLOCK_THREADS
        Py_XDECREF( sipCallMethod( NULL, a2, "D", &error, sipType_QString, NULL ) );
        SIP_UNBLOCK_THREADS
      } );

      sipRes = sipCpp;

      Py_END_ALLOW_THREADS
    }
%End

};
```

I end up with the following compilation errors

```
error: non-virtual member function marked 'override' hides virtual member function
     ::MyObject* doThings(const  ::QString&,const  ::QUrl&,PyObject *) SIP_OVERRIDE;

hidden overloaded virtual function 'MyInterface::doThings' declared here: type mismatch at 3rd parameter ('const std::function<void (const QString &)> &' vs 'PyObject *' (aka '_object *'))
           virtual MyObject *doThings( const QString &filePath, const QUrl &url,
                     ^
 error: allocating an object of abstract class type 'sipMyInterface'
            sipCpp = new sipMyInterface();
                         ^
note: unimplemented pure virtual method 'doThings' in 'sipMyInterface'
    virtual MyObject *doThings( const QString &filePath, const QUrl &url,
```

The errors make sense to me because the sip doThings version has not the
same signature than the original Cpp one (Callable vs std::function) but
I fail to understand how to fix this.

Kind regards,
Julien
Reply | Threaded
Open this post in threaded view
|

Re: SIP: Virtual pure method with std::function

Denis Rouzaud
Hi Julien,

For the sake of clarity, you might want to filter out what is specific to QGIS syntax, mainly all the SIP_* macros.
The sipify script is a QGIS internal and not known further.


On Thu, 1 Apr 2021, 11:58 Julien Cabieces, <[hidden email]> wrote:

Hi,

I would like to define an interface (abstract class) where one method is
pure virtual and takes a std::function as a parameter.

I have this in my C++ header

```
class MyInterface
{
  public:

#ifndef SIP_RUN
    virtual MyObject *doThings( const QString &filePath, const QUrl &url,
                                const std::function<void ( const QString & )> &errorCallback = {} ) = 0 ;
#else
    virtual MyObject *doThings( const QString &filePath, const QUrl &url, SIP_PYCALLABLE / AllowNone / ) = 0;
    % MethodCode
    {
      Py_BEGIN_ALLOW_THREADS

      sipCpp->MyInterface::doThings( *a0, *a1, [a2]( const QString  & error )
      {
        SIP_BLOCK_THREADS
        Py_XDECREF( sipCallMethod( NULL, a2, "D", &error, sipType_QString, NULL ) );
        SIP_UNBLOCK_THREADS
      } );

      sipRes = sipCpp;

      Py_END_ALLOW_THREADS
    }
    % End
#endif
```

and this in the corresponding sip file

```
class MyInterface
{

%TypeHeaderCode
#include "myinterface.h"
%End
  public:

    virtual MyObject *doThings( const QString &filePath, const QUrl &url, SIP_PYCALLABLE / AllowNone / ) = 0;
%MethodCode
    {
      Py_BEGIN_ALLOW_THREADS

      sipCpp->MyInterface::doThings( *a0, *a1, [a2]( const QString  & error )
      {
        SIP_BLOCK_THREADS
        Py_XDECREF( sipCallMethod( NULL, a2, "D", &error, sipType_QString, NULL ) );
        SIP_UNBLOCK_THREADS
      } );

      sipRes = sipCpp;

      Py_END_ALLOW_THREADS
    }
%End

};
```

I end up with the following compilation errors

```
error: non-virtual member function marked 'override' hides virtual member function
     ::MyObject* doThings(const  ::QString&,const  ::QUrl&,PyObject *) SIP_OVERRIDE;

hidden overloaded virtual function 'MyInterface::doThings' declared here: type mismatch at 3rd parameter ('const std::function<void (const QString &)> &' vs 'PyObject *' (aka '_object *'))
           virtual MyObject *doThings( const QString &filePath, const QUrl &url,
                     ^
 error: allocating an object of abstract class type 'sipMyInterface'
            sipCpp = new sipMyInterface();
                         ^
note: unimplemented pure virtual method 'doThings' in 'sipMyInterface'
    virtual MyObject *doThings( const QString &filePath, const QUrl &url,
```

The errors make sense to me because the sip doThings version has not the
same signature than the original Cpp one (Callable vs std::function) but
I fail to understand how to fix this.

Kind regards,
Julien
Reply | Threaded
Open this post in threaded view
|

Re: SIP: Virtual pure method with std::function

Phil Thompson-5
In reply to this post by jcabieces
On 01/04/2021 10:58, Julien Cabieces wrote:

> Hi,
>
> I would like to define an interface (abstract class) where one method
> is
> pure virtual and takes a std::function as a parameter.
>
> I have this in my C++ header
>
> ```
> class MyInterface
> {
>   public:
>
> #ifndef SIP_RUN
>     virtual MyObject *doThings( const QString &filePath, const QUrl
> &url,
>                                 const std::function<void ( const
> QString & )> &errorCallback = {} ) = 0 ;
> #else
>     virtual MyObject *doThings( const QString &filePath, const QUrl
> &url, SIP_PYCALLABLE / AllowNone / ) = 0;
>     % MethodCode
>     {
>       Py_BEGIN_ALLOW_THREADS
>
>       sipCpp->MyInterface::doThings( *a0, *a1, [a2]( const QString  &
> error )
>       {
>         SIP_BLOCK_THREADS
>         Py_XDECREF( sipCallMethod( NULL, a2, "D", &error,
> sipType_QString, NULL ) );
>         SIP_UNBLOCK_THREADS
>       } );
>
>       sipRes = sipCpp;
>
>       Py_END_ALLOW_THREADS
>     }
>     % End
> #endif
> ```
>
> and this in the corresponding sip file
>
> ```
> class MyInterface
> {
>
> %TypeHeaderCode
> #include "myinterface.h"
> %End
>   public:
>
>     virtual MyObject *doThings( const QString &filePath, const QUrl
> &url, SIP_PYCALLABLE / AllowNone / ) = 0;
> %MethodCode
>     {
>       Py_BEGIN_ALLOW_THREADS
>
>       sipCpp->MyInterface::doThings( *a0, *a1, [a2]( const QString  &
> error )
>       {
>         SIP_BLOCK_THREADS
>         Py_XDECREF( sipCallMethod( NULL, a2, "D", &error,
> sipType_QString, NULL ) );
>         SIP_UNBLOCK_THREADS
>       } );
>
>       sipRes = sipCpp;
>
>       Py_END_ALLOW_THREADS
>     }
> %End
>
> };
> ```
>
> I end up with the following compilation errors
>
> ```
> error: non-virtual member function marked 'override' hides virtual
> member function
>      ::MyObject* doThings(const  ::QString&,const  ::QUrl&,PyObject *)
> SIP_OVERRIDE;
>
> hidden overloaded virtual function 'MyInterface::doThings' declared
> here: type mismatch at 3rd parameter ('const std::function<void (const
> QString &)> &' vs 'PyObject *' (aka '_object *'))
>            virtual MyObject *doThings( const QString &filePath, const
> QUrl &url,
>                      ^
>  error: allocating an object of abstract class type 'sipMyInterface'
>             sipCpp = new sipMyInterface();
>                          ^
> note: unimplemented pure virtual method 'doThings' in 'sipMyInterface'
>     virtual MyObject *doThings( const QString &filePath, const QUrl
> &url,
> ```
>
> The errors make sense to me because the sip doThings version has not
> the
> same signature than the original Cpp one (Callable vs std::function)
> but
> I fail to understand how to fix this.

You need to provide the C++ signature in [] after the Python signature.
See the example in this section...

https://www.riverbankcomputing.com/static/Docs/sip/directives.html#std-directive-VirtualCatcherCode

...and you need to provide %VirtualCatcherCode.

Phil
Reply | Threaded
Open this post in threaded view
|

Re: SIP: Virtual pure method with std::function

jcabieces

Hi,

> You need to provide the C++ signature in [] after the Python signature.
> See the example in this section...

I change the signature in the sip file like this

virtual MyObject *doThings( const QString &filePath, const QUrl &url,
SIP_PYCALLABLE / AllowNone / ) = 0 [MyObject * ( const QString &, const
QUrl &, const ErrorCallback & )];

and define the ErrorCallback this way in my header

typedef std::function< void ( const QString & ) > ErrorCallback;

It fails later with this error

sip_corepart0.cpp:21379:193: error: unknown type name 'ErrorCallback'

my header file is included later (line 49811) in cpp file produced by
sip, so it's normal it doesn't know yet the ErrorCallback type.

If I get rid of the typedef definition and put the std::function
directly in the sip signature, sip fails with a syntax error. It looks
like it comes from the parenthesis around "const QString &".

Any idea?

Regards,
Julien


> On 01/04/2021 10:58, Julien Cabieces wrote:
>> Hi,
>>
>> I would like to define an interface (abstract class) where one method
>> is
>> pure virtual and takes a std::function as a parameter.
>>
>> I have this in my C++ header
>>
>> ```
>> class MyInterface
>> {
>>   public:
>>
>> #ifndef SIP_RUN
>>     virtual MyObject *doThings( const QString &filePath, const QUrl
>> &url,
>>                                 const std::function<void ( const
>> QString & )> &errorCallback = {} ) = 0 ;
>> #else
>>     virtual MyObject *doThings( const QString &filePath, const QUrl
>> &url, SIP_PYCALLABLE / AllowNone / ) = 0;
>>     % MethodCode
>>     {
>>       Py_BEGIN_ALLOW_THREADS
>>
>>       sipCpp->MyInterface::doThings( *a0, *a1, [a2]( const QString  &
>> error )
>>       {
>>         SIP_BLOCK_THREADS
>>         Py_XDECREF( sipCallMethod( NULL, a2, "D", &error,
>> sipType_QString, NULL ) );
>>         SIP_UNBLOCK_THREADS
>>       } );
>>
>>       sipRes = sipCpp;
>>
>>       Py_END_ALLOW_THREADS
>>     }
>>     % End
>> #endif
>> ```
>>
>> and this in the corresponding sip file
>>
>> ```
>> class MyInterface
>> {
>>
>> %TypeHeaderCode
>> #include "myinterface.h"
>> %End
>>   public:
>>
>>     virtual MyObject *doThings( const QString &filePath, const QUrl
>> &url, SIP_PYCALLABLE / AllowNone / ) = 0;
>> %MethodCode
>>     {
>>       Py_BEGIN_ALLOW_THREADS
>>
>>       sipCpp->MyInterface::doThings( *a0, *a1, [a2]( const QString  &
>> error )
>>       {
>>         SIP_BLOCK_THREADS
>>         Py_XDECREF( sipCallMethod( NULL, a2, "D", &error,
>> sipType_QString, NULL ) );
>>         SIP_UNBLOCK_THREADS
>>       } );
>>
>>       sipRes = sipCpp;
>>
>>       Py_END_ALLOW_THREADS
>>     }
>> %End
>>
>> };
>> ```
>>
>> I end up with the following compilation errors
>>
>> ```
>> error: non-virtual member function marked 'override' hides virtual
>> member function
>>      ::MyObject* doThings(const  ::QString&,const  ::QUrl&,PyObject *)
>> SIP_OVERRIDE;
>>
>> hidden overloaded virtual function 'MyInterface::doThings' declared
>> here: type mismatch at 3rd parameter ('const std::function<void (const
>> QString &)> &' vs 'PyObject *' (aka '_object *'))
>>            virtual MyObject *doThings( const QString &filePath, const
>> QUrl &url,
>>                      ^
>>  error: allocating an object of abstract class type 'sipMyInterface'
>>             sipCpp = new sipMyInterface();
>>                          ^
>> note: unimplemented pure virtual method 'doThings' in 'sipMyInterface'
>>     virtual MyObject *doThings( const QString &filePath, const QUrl
>> &url,
>> ```
>>
>> The errors make sense to me because the sip doThings version has not
>> the
>> same signature than the original Cpp one (Callable vs std::function)
>> but
>> I fail to understand how to fix this.
>
> You need to provide the C++ signature in [] after the Python signature.
> See the example in this section...
>
> https://www.riverbankcomputing.com/static/Docs/sip/directives.html#std-directive-VirtualCatcherCode
>
> ...and you need to provide %VirtualCatcherCode.
>
> Phil

Reply | Threaded
Open this post in threaded view
|

Re: SIP: Virtual pure method with std::function

jcabieces
In reply to this post by Denis Rouzaud

Hi Denis,

> For the sake of clarity, you might want to filter out what is specific to
> QGIS syntax, mainly all the SIP_* macros.
> The sipify script is a QGIS internal and not known further.

Indeed, I try to remove all the QGIS specifics, but I miss the SIP_*
macros.

Regards,
Julien

> Hi Julien,
>
> For the sake of clarity, you might want to filter out what is specific to
> QGIS syntax, mainly all the SIP_* macros.
> The sipify script is a QGIS internal and not known further.
>
>
> On Thu, 1 Apr 2021, 11:58 Julien Cabieces, <[hidden email]>
> wrote:
>
>>
>> Hi,
>>
>> I would like to define an interface (abstract class) where one method is
>> pure virtual and takes a std::function as a parameter.
>>
>> I have this in my C++ header
>>
>> ```
>> class MyInterface
>> {
>>   public:
>>
>> #ifndef SIP_RUN
>>     virtual MyObject *doThings( const QString &filePath, const QUrl &url,
>>                                 const std::function<void ( const QString &
>> )> &errorCallback = {} ) = 0 ;
>> #else
>>     virtual MyObject *doThings( const QString &filePath, const QUrl &url,
>> SIP_PYCALLABLE / AllowNone / ) = 0;
>>     % MethodCode
>>     {
>>       Py_BEGIN_ALLOW_THREADS
>>
>>       sipCpp->MyInterface::doThings( *a0, *a1, [a2]( const QString  &
>> error )
>>       {
>>         SIP_BLOCK_THREADS
>>         Py_XDECREF( sipCallMethod( NULL, a2, "D", &error, sipType_QString,
>> NULL ) );
>>         SIP_UNBLOCK_THREADS
>>       } );
>>
>>       sipRes = sipCpp;
>>
>>       Py_END_ALLOW_THREADS
>>     }
>>     % End
>> #endif
>> ```
>>
>> and this in the corresponding sip file
>>
>> ```
>> class MyInterface
>> {
>>
>> %TypeHeaderCode
>> #include "myinterface.h"
>> %End
>>   public:
>>
>>     virtual MyObject *doThings( const QString &filePath, const QUrl &url,
>> SIP_PYCALLABLE / AllowNone / ) = 0;
>> %MethodCode
>>     {
>>       Py_BEGIN_ALLOW_THREADS
>>
>>       sipCpp->MyInterface::doThings( *a0, *a1, [a2]( const QString  &
>> error )
>>       {
>>         SIP_BLOCK_THREADS
>>         Py_XDECREF( sipCallMethod( NULL, a2, "D", &error, sipType_QString,
>> NULL ) );
>>         SIP_UNBLOCK_THREADS
>>       } );
>>
>>       sipRes = sipCpp;
>>
>>       Py_END_ALLOW_THREADS
>>     }
>> %End
>>
>> };
>> ```
>>
>> I end up with the following compilation errors
>>
>> ```
>> error: non-virtual member function marked 'override' hides virtual member
>> function
>>      ::MyObject* doThings(const  ::QString&,const  ::QUrl&,PyObject *)
>> SIP_OVERRIDE;
>>
>> hidden overloaded virtual function 'MyInterface::doThings' declared here:
>> type mismatch at 3rd parameter ('const std::function<void (const QString
>> &)> &' vs 'PyObject *' (aka '_object *'))
>>            virtual MyObject *doThings( const QString &filePath, const QUrl
>> &url,
>>                      ^
>>  error: allocating an object of abstract class type 'sipMyInterface'
>>             sipCpp = new sipMyInterface();
>>                          ^
>> note: unimplemented pure virtual method 'doThings' in 'sipMyInterface'
>>     virtual MyObject *doThings( const QString &filePath, const QUrl &url,
>> ```
>>
>> The errors make sense to me because the sip doThings version has not the
>> same signature than the original Cpp one (Callable vs std::function) but
>> I fail to understand how to fix this.
>>
>> Kind regards,
>> Julien
>>

Reply | Threaded
Open this post in threaded view
|

Re: SIP: Virtual pure method with std::function

Phil Thompson-5
In reply to this post by jcabieces
On 06/04/2021 13:24, Julien Cabieces wrote:

> Hi,
>
>> You need to provide the C++ signature in [] after the Python
>> signature.
>> See the example in this section...
>
> I change the signature in the sip file like this
>
> virtual MyObject *doThings( const QString &filePath, const QUrl &url,
> SIP_PYCALLABLE / AllowNone / ) = 0 [MyObject * ( const QString &, const
> QUrl &, const ErrorCallback & )];
>
> and define the ErrorCallback this way in my header
>
> typedef std::function< void ( const QString & ) > ErrorCallback;
>
> It fails later with this error
>
> sip_corepart0.cpp:21379:193: error: unknown type name 'ErrorCallback'
>
> my header file is included later (line 49811) in cpp file produced by
> sip, so it's normal it doesn't know yet the ErrorCallback type.
>
> If I get rid of the typedef definition and put the std::function
> directly in the sip signature, sip fails with a syntax error. It looks
> like it comes from the parenthesis around "const QString &".
>
> Any idea?

SIP doesn't implement a full C++ parser and there will be some
constructs that it can't handle, in this case the arguments to the
std::function template. (Maybe putting a dummy name after the void would
work.)

A separate issue might be that SIP is being too aggressive when parsing
the C++ signature and maybe it could/should not try to resolve
ErrorCallback and leave it to the C++ compiler to determine if it has a
proper definition.

For the moment I can't suggest anything other than not using
std::function.

Phil
Reply | Threaded
Open this post in threaded view
|

Re: SIP: Virtual pure method with std::function

jcabieces

Hi,

Thank you for your reponse, I'm gonna considering doing it an other way.

Regards,
Julien


> On 06/04/2021 13:24, Julien Cabieces wrote:
>> Hi,
>>
>>> You need to provide the C++ signature in [] after the Python
>>> signature.
>>> See the example in this section...
>>
>> I change the signature in the sip file like this
>>
>> virtual MyObject *doThings( const QString &filePath, const QUrl &url,
>> SIP_PYCALLABLE / AllowNone / ) = 0 [MyObject * ( const QString &, const
>> QUrl &, const ErrorCallback & )];
>>
>> and define the ErrorCallback this way in my header
>>
>> typedef std::function< void ( const QString & ) > ErrorCallback;
>>
>> It fails later with this error
>>
>> sip_corepart0.cpp:21379:193: error: unknown type name 'ErrorCallback'
>>
>> my header file is included later (line 49811) in cpp file produced by
>> sip, so it's normal it doesn't know yet the ErrorCallback type.
>>
>> If I get rid of the typedef definition and put the std::function
>> directly in the sip signature, sip fails with a syntax error. It looks
>> like it comes from the parenthesis around "const QString &".
>>
>> Any idea?
>
> SIP doesn't implement a full C++ parser and there will be some
> constructs that it can't handle, in this case the arguments to the
> std::function template. (Maybe putting a dummy name after the void would
> work.)
>
> A separate issue might be that SIP is being too aggressive when parsing
> the C++ signature and maybe it could/should not try to resolve
> ErrorCallback and leave it to the C++ compiler to determine if it has a
> proper definition.
>
> For the moment I can't suggest anything other than not using
> std::function.
>
> Phil