wlav / cppyy

Other
404 stars 42 forks source link

Template class + SFINAE member function with variadic template arguments #201

Open taehyounpark opened 11 months ago

taehyounpark commented 11 months ago

This is related to #192 which was displaying the issue inside a large library + older version of cppyy used by ROOT. I've managed to isolate the issue into a minimal example on the latest version of cppyy.

Python 3.11.4 (main, Jul  5 2023, 08:41:25) [Clang 14.0.6 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import cppyy
>>> cppyy.__version__
'3.0.0'

The issue seems to be stemming from the fact that cppyy cannot handle a function that is:

  1. A member function of a template class, that
  2. Accepts a template parameter pack, and
  3. Is also overloaded via SFINAE depending on the class template parameter

This (as far as I know) is valid C++ according to both GCC and Clang, including whatever is being used by the backend of cppyy. Here is the illustration of the issue:

import cppyy

cppyy.cppdef('''
template <typename T> struct TestSfinae {
  template <typename U, typename V = T,
            std::enable_if_t<std::is_arithmetic_v<V>, bool> = false>
  auto test_single(U arg) {
    std::cout << "good" << std::endl;
    return 0;
  }
  template <typename... Args, typename V = T,
            std::enable_if_t<std::is_arithmetic_v<V>, bool> = false>
  auto test_pack(Args... args) {
    std::cout << "good" << std::endl;
    return 0;
  }
};
''')

# call from cling
cppyy.cppdef('''auto testing = TestSfinae<float>();''')
cppyy.cppdef('''auto single_call = testing.test_single(1);''')  # good
cppyy.cppdef('''auto pack_call = testing.test_pack(1,2,3);''')  # good

# call from bindings
testing = cppyy.gbl.TestSfinae['float']()
testing.test_single(1)  # good
testing.test_pack(1,2,3)  # fails

Which fails with the message

input_line_24:6:70: error: no matching member function for call to 'test_pack'
      new (ret) (int) (((TestSfinae<float>*)obj)->TestSfinae<float>::test_pack<int, int, int, float, false>(*(int*)args[0], *(int*)args[1],
                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
input_line_18:11:8: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Args'
  auto test_pack(Args... args) {
       ^

Which (again, seems to me) has to do with cppyy explicitly providing the SFINAE template parameters somewhere in the process of passing the call down to its C++-binding.

wlav commented 11 months ago

That code that doesn't compile is generated in CallFunc (ie. ROOT/meta, a portion of which lives in cppyy-backend, yes, hence this problem also shows up in cppyy master). CallFunc reconstructs the function name (incl. template parameters) from the decl, not from any parameters provided by cppyy. Probably the code generator should check if a method is variadic and then not add the explicit template parameters.

Still recommended to report this for the ROOT repo. I'll probably fix it on my end b/c it affects cppyy proper, but per CERN rules, the ROOT team can not accept patches to ROOT code from me, so they'll have to come up with their own fix.

taehyounpark commented 11 months ago

Thank you for the quick reply and insight!