How to determine the type of a function parameter given the type of argument passed to it?

后端 未结 3 2232
借酒劲吻你
借酒劲吻你 2021-02-19 11:31

I need a type trait which will report the type of a functor\'s operator() parameter given the type of the functor and the type of an argument passed to it. Basical

相关标签:
3条回答
  • 2021-02-19 12:17

    To get started I would go with this:

    template<typename F>
    struct parameter_type_impl;
    
    // may be with variadic arguments
    template<typename R, typename A, typename F>
    struct parameter_type_impl<R (F::*)(A)> {
      typedef A type;
    };
    
    template<typename F>
    struct parameter_type {
      typedef typename parameter_type_impl<decltype(&F::operator())>::type type;
    };
    

    I don't see why you would pass in the actual argument type. If the conversion is not able to take place you have to use special measures (e.g. SFINAE) later on. I think the two things are orthogonal: deducing the argument type, then deciding if the argument you would like to pass in is convertible.

    The non-C++03 decltype is hard to get rid of. Specifying a function type always requires knowledge of the arguments. As soon as you would spell out the arguments, the whole thing would be moot.

    The same problem would occur with Boost.Function Types.

    0 讨论(0)
  • 2021-02-19 12:19

    I am afraid that this is not exactly possible without help from your client.

    TL;DR: unit test fail (grrr gcc).

    The general case of your question is this functor:

    struct Functor {
      template <typename T>
      typename std::enable_if<std::is_integral<T>::value>::type
      operator()(T t) const;
    
      void operator(double d) const;
    };
    

    It combines the two main issues here:

    1. If there is an overload, then taking &F::operator() requires a static_cast to a given type to disambiguate which overload should be used
    2. Templates (and arbitrary conditions to express them) cannot be succintly expressed as typedefs

    Therefore, the client (Functor here) need to provide additional hooks for you if you truly wish to get this type. And without decltype I don't see how to get it (note, gcc provides typeof as an extension in C++03).

    Getting the client to give us hints:

    // 1. Make use of the return value:
    struct Functor {
      template <typename T>
      typename std::enable_if<std::is_integral<T>::value, T>::type
      operator()(T t) const;
    
      double operator(double d) const;
    };
    
    // 2. Double up the work (but leave the return value as is)
    struct Functor {
      template <typename T>
      static typename std::enable_if<std::is_integral<T>::value, T>::type Select(T);
    
      static double Select(T);
    
      template <typename T>
      typename std::enable_if<std::is_integral<T>::value>::type
      operator()(T t) const;
    
      void operator(double d) const;
    };
    

    Let's say we go for the second case (leaving the return value free for another use).

    template <typename F, typename T>
    struct parameter {
      static T t;
      typedef decltype(F::Select(t)) type;
    };
    

    In C++03, replace decltype by typeof with gcc.

    I don't see a way to forego decltype. sizeof does provides an unevaluated context but it does not seem to help much here.

    Unit Tests Here.

    Unfortunately, there is a gcc bug it seems with the references, and float& gets reduced to float (and any other reference really), the bug remains with decltype so it's just a buggy implementation :/ Clang 3.0 has no problem with the C++11 version (decltype) but does not implement typeof I think.

    This can be worked around by requiring the client to use a ref<float> class instead, and then unwrapping it. Just a bit more burden...

    0 讨论(0)
  • 2021-02-19 12:23
        #include <iostream>
    
        template< typename PParameter00 = void, typename PParameter01 = void, typename PParameter02 = void, typename PParameter03 = void >
        struct TIdentityParameter // Users need to inherit from it. Add more types as needed.
        {
          typedef PParameter00 TType00;
          typedef PParameter01 TType01;
          typedef PParameter02 TType02;
          typedef PParameter03 TType03;
        };
    
        struct TUserFunctor00 : public TIdentityParameter< float const &, int, void * >
        {
          void operator()( float const &, int, void * );
          // or they can do
          //void operator()( TType00, TType01, TType02 );
        };
    
        struct TUserFunctor01 : public TIdentityParameter< char const *, double >
        {
          void operator()( char const*, double );
          // or they can do
          //void operator()( TType00, TType01 );
        };
    
        template< bool pValue >
        struct TValueBool
        {
          static bool const sValue = pValue;
        };
    
        template< typename PType00, typename PType01 >
        struct TIsSame : public TValueBool< false >
        {
        };
    
        template< typename PType >
        struct TIsSame< PType, PType > : public TValueBool< true >
        {
        };
    
        int main( void )
        {
         std::cout << TIsSame< TUserFunctor00::TType02, void * >::sValue << std::endl;
         std::cout << TIsSame< TUserFunctor01::TType00, double >::sValue << std::endl;
    
         return ( 0 );
        }
    
    Code on [ideone][1]. I don't think it's asking too much from users to inherit from your struct in a pattern explained to them. After all, they want to work with your library. Anyway, maybe it's not what you are looking for.
    

    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    EDIT: Here is something, maybe, a bit closer to the functionality JAred is looking for, but, I understand, the style does not appeal to him. Although, within C++03, I don't see how you can do it differently. Note, you can make TIdentityParameter take, say 16 template arguments to cover 16 possible types. Once again, yes, user has to inherit and specify types. Ideone:

    #include <iostream>
    
    struct TOneCrazyStruct
    {
    };
    
    template< typename PParameter00 = TOneCrazyStruct, typename PParameter01 = TOneCrazyStruct, typename PParameter02 = TOneCrazyStruct,
      typename PParameter03 = TOneCrazyStruct, typename PParameter04 = TOneCrazyStruct >
    struct TIdentityParameter //Users will need to inherit from this struct as shown below.
    {
      typedef PParameter00 TType00;
      typedef PParameter01 TType01;
      typedef PParameter02 TType02;
      typedef PParameter03 TType03;
      typedef PParameter04 TType04;
    };
    
    struct TUserFunctor00 : public TIdentityParameter< float const &, int, void *, double >
    {
      void operator()( float const &, int, void * );
      void operator()( double );
    };
    
    template< bool pValue >
    struct TValueBool
    {
      static bool const sValue = pValue;
    };
    
    template< typename PType00, typename PType01 >
    struct TIsSame : public TValueBool< false >
    {
    };
    
    template< typename PType >
    struct TIsSame< PType, PType > : public TValueBool< true >
    {
    };
    
    template< typename PFunctor, typename PParameter >
    struct THasType : public TValueBool<
      TIsSame< typename PFunctor::TType00, PParameter >::sValue || TIsSame< typename PFunctor::TType01, PParameter >::sValue
        || TIsSame< typename PFunctor::TType02, PParameter >::sValue || TIsSame< typename PFunctor::TType03, PParameter >::sValue >
    {
    };
    
    int main( void )
    {
     std::cout << THasType< TUserFunctor00, void * >::sValue << std::endl;
     std::cout << THasType< TUserFunctor00, long double >::sValue << std::endl;
    
     return ( 0 );
     }
    
    0 讨论(0)
提交回复
热议问题