Skip to content

Supporting C++11 forwarding references #3814

@shwina

Description

@shwina

Based on the discussion in #3790 I'm looking into adding support for C++11 forwarding references. I'm looking for guidance on the better of two approaches to take for doing this.

Let me explain the problem and possible solutions with an example:

#distutils: language=c++                                                                                                                                                          
#distutils: extra_compile_args=-std=c++11                                                                                                                                    
                                                                                                                                                                                  
cdef extern from *:                                                                                                                                                               
    """                                                                                                                                                                           
    #include <iostream>                                                                                                                                                           
                                                                                                                                                                                  
    void f(int&& t) {                                                                                                                                                             
        std::cout << "Rvalue" << std::endl;                                                                                                                                       
    }                                                                                                                                                                             
                                                                                                                                                                                  
    void f(int& t) {                                                                                                                                                              
        std::cout << "Lvalue" << std::endl;                                                                                                                                       
    }                                                                                                                                                                             
                                                                                                                                                                                  
    void f(const int& t) {                                                                                                                                                        
        std::cout << "Const lvalue" << std::endl;                                                                                                                                 
    }                                                                                                                                                                             
                                                                                                                                                                                  
    template <typename T>                                                                                                                                                         
    void foo(T&& t) {                                                                                                                                                             
        f(std::forward<T>(t));                                                                                                                                                    
    }                                                                                                                                                                             
    """                                                                                                                                                                           
    cdef void foo[T](T&& t)                                                                                                                                                       
                                                                                                                                                                                  
cdef int x = 1
foo(1)                                                                                                                                                                            
foo(x)                                                                                                                                                                                  

Here, foo is a function that accepts a forwarding reference. Based on whether the argument to foo is an lvalue or rvalue, it should dispatch to the appropriate overload of f. Thus, the expected output of this program is:

Rvalue
Lvalue

The C++ code generated by Cython for the above currently looks like this:

  __pyx_v_10forwarding_x = 1;                                                                                                                                                                                                                                                                                                                 
  foo<long>(1);                                                                                                                                                                                                                                                                                                                    
  foo<int>(__pyx_v_10forwarding_x);       // this line is incorrect currently

Because foo accepts a forwarding reference, the template argument should be of the form:

  • int& if the corresponding function argument is an lvalue expression
  • int if the corresponding function argument is an rvalue expression

One solution is to let decltype handle producing the correct template argument. In this case, the C++ code would look like this::

foo<decltype((1))>(1);                                                                                                                                                                                                                                                                                                                    
foo<decltype((__pyx_v_10forwarding_x))>(__pyx_v_10forwarding_x);      

The other solution is for Cython to compute the appropriate template argument a priori. In this case, the C++ code would look like this:

foo<long>(1);                                                                                                                                                                                                                                                                                                                    
foo<int&>(__pyx_v_10forwarding_x);  

Which of these sounds better from a Cython viewpoint?

cc: @da-woods if you have any suggestions here. Thank you :)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions