Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

boost python template argument deduction/substitution failed when exporting function

I'm trying to export two overloaded functions to Python. So I first define the pointers to these functions and then I use them to expose the functions to Python.

BOOST_PYTHON_MODULE(mylib){

    // First define pointers to overloaded function
    double (*expt_pseudopot02_v1)(double,double,double,const VECTOR&,
                                  int,int,int,double,const VECTOR&,
                                  int,int,int,double,const VECTOR& ) = &pseudopot02;

    boost::python::list (*expt_pseudopot02_v2)(double, double, double, const VECTOR&,
                            int,int,int,double, const VECTOR&,
                            int,int,int,double, const VECTOR&,  int, int ) = &pseudopot02;

    // Now export 
    def("pseudopot02", expt_pseudopot02_v1); // this works fine!
    //def("pseudopot02", expt_pseudopot02_v2); // this one gives the problem!

}

The first export function works fine. The second (presently commented) fails, giving the error:

template argument deduction/substitution failed

it also prints this explanation:

...../boost_1_50_0/boost/python/make_function.hpp:104:59: note:   mismatched types ‘RT (ClassT::*)(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14)const volatile’ and ‘boost::python::list (*)(double, double, double, const VECTOR&, int, int, int, double, const VECTOR&, int, int, int, double, const VECTOR&, int, int)’
     f,default_call_policies(), detail::get_signature(f));
                                                       ^

which doesn't tell me much except for the general idea that there is something with function signature. So don't have any idea on the nature of the problem and hence how to fix it. It doesn't seem there was a similar problem discussed here either.

Edit: Here I provide requested minimal,complete, verifiable code:

In file libX.cpp

#include <boost/python.hpp>
#include "PP.h"
using namespace boost::python;

#ifdef CYGWIN
BOOST_PYTHON_MODULE(cygX){
#else
BOOST_PYTHON_MODULE(libX){
#endif

// This set will work!
//  double (*expt_PP_v1)(const VECTOR& ) = &PP;
//  boost::python::list (*expt_PP_v2)(const VECTOR&,int) = &PP;

// This one - only the first function (returning double)
// the function returning boost::python::list object causes the error
  double (*expt_PP_v1)(double,double,double,const VECTOR&,int,int,int,double,const VECTOR&,int,int,int,double,const VECTOR&) = &PP;
  boost::python::list (*expt_PP_v2)(double, double, double, const VECTOR&, int,int,int,double, const VECTOR&, int,int,int,double, const VECTOR&,  int, int ) = &PP;

  def("PP", expt_PP_v1);
  def("PP", expt_PP_v2);
}

File PP.h

#ifndef PP_H
#define PP_H
#include <boost/python.hpp>
using namespace boost::python;

class VECTOR{
  public:
  double x,y,z;
  VECTOR(){ x = y = z = 0.0; }
 ~VECTOR(){  }
  VECTOR& operator=(const double &v){  x=y=z=v;    return *this;  }
};

/*  This set of functions will work
double PP(const VECTOR& R, int is_derivs,VECTOR& dIdR );
boost::python::list PP(const VECTOR& R,int is_derivs);
double PP(const VECTOR& R );
*/

// The following - will not, only the one returning double
double PP(double C0, double C2, double alp, const VECTOR& R,
      int nxa,int nya, int nza, double alp_a, const VECTOR& Ra,
      int nxb,int nyb, int nzb, double alp_b, const VECTOR& Rb,
      int is_normalize, 
      int is_derivs, VECTOR& dIdR, VECTOR& dIdA, VECTOR& dIdB
     );
boost::python::list PP(double C0, double C2, double alp, const VECTOR& R,
                   int nxa,int nya, int nza, double alp_a, const VECTOR& Ra,
                   int nxb,int nyb, int nzb, double alp_b, const VECTOR& Rb,
                   int is_normalize, int is_derivs
                  );

double PP(double C0, double C2, double alp, const VECTOR& R,
      int nxa,int nya, int nza, double alp_a, const VECTOR& Ra,
      int nxb,int nyb, int nzb, double alp_b, const VECTOR& Rb
     );

#endif // PP_H

In file PP.cpp

#include "PP.h"
/*  This set will work
double PP(const VECTOR& R, int is_derivs,VECTOR& dIdR ){  dIdR = 0.0;  return 0.0; }
boost::python::list PP(const VECTOR& R,int is_derivs){
  VECTOR dIdR;  
  double I = PP(R, is_derivs, dIdR);
  boost::python::list res;
  res.append(0.0);   if(is_derivs){  res.append(dIdR); }
  return res;
}

double PP(const VECTOR& R ){  VECTOR dIdR;  double res = PP(R, 0, dIdR);  return res; } 
*/

// The following functions will not always work
double PP(double C0, double C2, double alp, const VECTOR& R,
      int nxa,int nya, int nza, double alp_a, const VECTOR& Ra,
      int nxb,int nyb, int nzb, double alp_b, const VECTOR& Rb,
      int is_normalize, 
      int is_derivs, VECTOR& dIdR, VECTOR& dIdA, VECTOR& dIdB
     ){  dIdR = 0.0;  dIdA = 0.0;  dIdB = 0.0;  return 0.0;  }

boost::python::list PP(double C0, double C2, double alp, const VECTOR& R,
                   int nxa,int nya, int nza, double alp_a, const VECTOR& Ra,
                   int nxb,int nyb, int nzb, double alp_b, const VECTOR& Rb,
                   int is_normalize, int is_derivs
                  ){
  VECTOR dIdA, dIdR, dIdB;
  double I = PP(C0,C2,alp,R, nxa,nya,nza,alp_a,Ra, nxb,nyb,nzb,alp_b,Rb, is_normalize,is_derivs,dIdR,dIdA,dIdB); 

  boost::python::list res;
  res.append(I); 
  if(is_derivs){   res.append(dIdR);   res.append(dIdA);  res.append(dIdB);  }
  return res; 
}

double PP(double C0, double C2, double alp, const VECTOR& R,
      int nxa,int nya, int nza, double alp_a, const VECTOR& Ra,
      int nxb,int nyb, int nzb, double alp_b, const VECTOR& Rb
     ){

  VECTOR dIdR,dIdA,dIdB;
  double res = PP(C0, C2, alp, R, nxa,nya,nza,alp_a,Ra, nxb,nyb,nzb,alp_b,Rb, 1, 0, dIdR, dIdA, dIdB);
  return res;
} 

So, it look to me that the recognition of template gets screwed when the number of parameters is large. I checked several times that signatures in libX.cpp, PP.cpp and PP.h match among themselves and do not overlap with those of overloaded functions. So, I'm still have no clue what is the source of the problem.

like image 878
user938720 Avatar asked Sep 22 '26 19:09

user938720


1 Answers

In short, the functions being exposed exceed the default maximum arity of 15. As noted in the configuration documentation, one can define BOOST_PYTHON_MAX_ARITY to control the maximum allowed arity of any function, member function, or constructor being wrapped and exposed through Boost.Python. In this particular case, one of the overloads has an arity of 16, so one could define the max arity before including boost/python.hpp:

#define BOOST_PYTHON_MAX_ARITY 16
#include <boost/python.hpp>

As of the time of this writing, Boost.Python (1.58) does not use C++11's variadic templates. Instead, if uses preprocessor macro expansions to provide template specializations and allows users to configure maximum arity through the BOOST_PYTHON_MAX_ARITY macro.


Here is a complete minimal example demonstrating increasing the max arity:

#define BOOST_PYTHON_MAX_ARITY 16
#include <boost/python.hpp>

// Functions have 5 parameters per line.

/// @brief Mockup spam function with 14 parameters.
double spam(
  int, int, int, int, int, // 5
  int, int, int, int, int, // 10
  int, int, int, int       // 14
)
{
  return 42;
}

/// @brief Mockup spam function with 16 parameters.
boost::python::list spam(
  int, int, int, int, int, // 5
  int, int, int, int, int, // 10
  int, int, int, int, int, // 15
  int                      // 16
)
{
  boost::python::list list;
  return list;
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;

  double (*spam_14)(
    int, int, int, int, int, // 5
    int, int, int, int, int, // 10
    int, int, int, int       // 14
  ) = &spam;

  python::list (*spam_16)(
    int, int, int, int, int, // 5
    int, int, int, int, int, // 10
    int, int, int, int, int, // 15
    int                      // 16
  ) = &spam;

  python::def("spam", spam_14);
  python::def("spam", spam_16);
}

Interactive usage:

>>> import example
>>> assert 42 == example.spam(*range(14))
>>> assert isinstance(example.spam(*range(16)), list)
>>> print example.spam.__doc__
spam( (int)arg1, (int)arg2, (int)arg3, (int)arg4, (int)arg5,
      (int)arg6, (int)arg7, (int)arg8, (int)arg9, (int)arg10,
      (int)arg11, (int)arg12, (int)arg13, (int)arg14) -> float :

    C++ signature :
        double spam(int,int,int,int,int,
                    int,int,int,int,int,
                    int,int,int,int)

spam( (int)arg1, (int)arg2, (int)arg3, (int)arg4, (int)arg5,
      (int)arg6, (int)arg7, (int)arg8, (int)arg9, (int)arg10,
      (int)arg11, (int)arg12, (int)arg13, (int)arg14, (int)arg15,
      (int)arg16) -> list :

    C++ signature :
        boost::python::list spam(int,int,int,int,int,
                                 int,int,int,int,int,
                                 int,int,int,int,int,
                                 int)

Without defining the max arity, the same code fails to compile:

/usr/local/include/boost/python/make_function.hpp:104:36: error: no matching
  function for call to 'get_signature'
        f,default_call_policies(), detail::get_signature(f));
                                   ^~~~~~~~~~~~~~~~~~~~~
  ... failed template argument deduction
like image 145
Tanner Sansbury Avatar answered Sep 25 '26 08:09

Tanner Sansbury



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!