1

I am trying to write a generic wrapper for building out a Python function from a C++ driver. To start I want to be able to just use some template magic to be able to generate the associated ctypes and be able to print them. I have everything sort of working, but I cannot seem to pull the function signature types a layer up, which defeats the purpose...

I currently have the following minimal reproduction of the issue, also on godbolt:

#include <functional>
#include <iostream>

template <typename T>
struct ctype
{
    using type = void*;
    static constexpr char name[]  = "c_void_p";
};

template <>
struct ctype<int>
{
    static constexpr char name[]  = "c_int";
};

template <>
struct ctype<double>
{
    static constexpr char name[]  = "c_double";
};

template<typename T> 
struct function_traits;  

template<typename R, typename ...Args> 
struct function_traits<std::function<R(Args...)>>
{
    static constexpr size_t nargs = sizeof...(Args);

    using result_ctype = ctype<R>;

    template <size_t i>
    using arg_ctype = ctype<typename std::tuple_element<i, std::tuple<Args...>>::type>;
};

template <typename FuncSignature>
void log_function_signature()
{
    // using function_proto = function_traits<std::function<double(double,int)>>; // OK
    using function_proto = function_traits<std::function<FuncSignature>>; // BAD? Why?
    std::cout << function_proto::result_ctype::name << std::endl;
    std::cout << function_proto::arg_ctype<0>::name << std::endl;
    std::cout << function_proto::arg_ctype<1>::name << std::endl;
}

double foobar(double x, int y)
{
    return x * y;
}

int main() {
    log_function_signature<double(double,int)>();
}

The function_traits was adapted from this answer: https://stackoverflow.com/a/9065203/2242096. Ideally I would just use FuncSignature directly inside of log_function_signature, but I cannot figure out why this is not allowed. The compiler gives me the following:

<source>: In function 'void log_function_signature()':
<source>:42:48: error: '::name' has not been declared; did you mean 'rename'?
   42 |     std::cout << function_proto::arg_ctype<0>::name << std::endl;
      |                                                ^~~~
      |                                                rename
<source>:43:48: error: '::name' has not been declared; did you mean 'rename'?
   43 |     std::cout << function_proto::arg_ctype<1>::name << std::endl;
      |                                                ^~~~
      |                                                rename
<source>: In instantiation of 'void log_function_signature() [with FuncSignature = double(double, int)]':
<source>:52:48:   required from here
<source>:42:34: error: dependent-name 'function_proto::arg_ctype' is parsed as a non-type, but instantiation yields a type
   42 |     std::cout << function_proto::arg_ctype<0>::name << std::endl;
      |                                  ^~~~~~~~~
<source>:42:34: note: say 'typename function_proto::arg_ctype' if a type is meant
<source>:43:34: error: dependent-name 'function_proto::arg_ctype' is parsed as a non-type, but instantiation yields a type
   43 |     std::cout << function_proto::arg_ctype<1>::name << std::endl;
      |                                  ^~~~~~~~~
<source>:43:34: note: say 'typename function_proto::arg_ctype' if a type is meant
ASM generation compiler returned: 1
<source>: In function 'void log_function_signature()':
<source>:42:48: error: '::name' has not been declared; did you mean 'rename'?
   42 |     std::cout << function_proto::arg_ctype<0>::name << std::endl;
      |                                                ^~~~
      |                                                rename
<source>:43:48: error: '::name' has not been declared; did you mean 'rename'?
   43 |     std::cout << function_proto::arg_ctype<1>::name << std::endl;
      |                                                ^~~~
      |                                                rename
<source>: In instantiation of 'void log_function_signature() [with FuncSignature = double(double, int)]':
<source>:52:48:   required from here
<source>:42:34: error: dependent-name 'function_proto::arg_ctype' is parsed as a non-type, but instantiation yields a type
   42 |     std::cout << function_proto::arg_ctype<0>::name << std::endl;
      |                                  ^~~~~~~~~
<source>:42:34: note: say 'typename function_proto::arg_ctype' if a type is meant
<source>:43:34: error: dependent-name 'function_proto::arg_ctype' is parsed as a non-type, but instantiation yields a type
   43 |     std::cout << function_proto::arg_ctype<1>::name << std::endl;
      |                                  ^~~~~~~~~
<source>:43:34: note: say 'typename function_proto::arg_ctype' if a type is meant
Execution build compiler returned: 1

Why does the template type work when written inside of the function, but not when passed in through a template?

Lnk2past
  • 13
  • 3
  • 1
    You need to write `function_proto::template arg_ctype<0>::name`. – cigien Jan 01 '21 at 00:20
  • The compiler errors are quite misleading here. – Quimby Jan 01 '21 at 00:28
  • @cigien thank you! That certainly did it. Just read through [this](https://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords) and I think I understand why... been wracking my brain all day trying to reason through this and apparently just could not properly search the issue. Thanks again! – Lnk2past Jan 01 '21 at 00:29
  • 1
    @Quimby It's not obvious, but actually in this case there's an error that says `error: dependent-name 'function_proto::arg_ctype' is parsed as a non-type, but instantiation yields a type` which hints at the problem. The suggested fix `say 'typename function_proto::arg_ctype' if a type is meant` is not what is meant here unfortunately, so I suppose it is misleading :( – cigien Jan 01 '21 at 00:33

0 Answers0