Search code examples
c++templatesc++11template-classes

function as template parameter : if(T receive 2 param)T(a,b); else T(a);


How to make template class Collection<K,T> receive a function T - that can either has signature T(K) or T(K,int) - as template argument, then conditionally compile base on the signature of the function?

Here is the existing code that can receive 1 signature : Collection<K,HashFunction(K)>.

template<typename AA> using HashFunction= HashStruct& (*)(AA );
/** This class is currently used in so many places in codebase. */
template<class K,HashFunction<K> T> class Collection{
    void testCase(){
        K k=K();
        HashStruct& hh= T(k);                     /*Collection1*/
        //.... something complex ...
    }
};

I want it to also support Collection<K,HashFunction(K,int)>.

template<class K,HashFunction<K> T /* ??? */> class Collection{
    int indexHash=1245323;
    void testCase(){
        K k=K();
        if(T receive 2 parameter){        // ???
             HashStruct& hh=T(k,this->indexHash);  /*Collection2*/   // ???
               //^ This is the heart of what I really want to achieve.
               //.... something complex (same) ...
        }else{
             HashStruct& hh=T(k);                  /*Collection1*/
               //.... something complex (same) ... 
        }
    }
};

Do I have no choice but to create 2 different classes : Collection1 & Collection2?
Answer that need more than c++11 is ok but less preferable.

I feel that it might be solvable by using "default parameter" trick.


Solution

  • Variadic templates, partial specialization and SFINAE can help you.

    If you accept to duplicate the test() method, you can do something like

    #include <iostream>
    
    using HashStruct = std::size_t;
    
    template<typename ... AA>
    using HashFunction = HashStruct & (*)(AA ... );
    
    HashStruct &  hf1 (std::size_t s)
     { static HashStruct val {0U}; return val = s; }
    
    HashStruct &  hf2 (std::size_t s, int i)
     { static HashStruct val {0U}; return val = s + std::size_t(i); }
    
    template <typename Tf, Tf F>
    class Collection;
    
    template <typename K, typename ... I, HashFunction<K, I...> F>
    class Collection<HashFunction<K, I...>, F>
     {
       public: 
    
          template <std::size_t N = sizeof...(I)>
          typename std::enable_if<N == 0U, void>::type test ()
           {
             K k=K();
    
             HashStruct & hh = F(k);
    
             std::cout << "case 0 (" << hh << ")" << std::endl;
           }
    
          template <std::size_t N = sizeof...(I)>
          typename std::enable_if<N == 1U, void>::type test ()
           {
             K k=K();
    
             HashStruct & hh = F(k, 100);
    
             std::cout << "case 1 (" << hh << ")" << std::endl;
           }
     };
    
    int main ()
     {
       Collection<HashFunction<std::size_t>, hf1>       c1;
       Collection<HashFunction<std::size_t, int>, hf2>  c2;
    
       c1.test(); // print "case 0 (0)"
       c2.test(); // print "case 1 (100)"
     }
    

    But, if you can pass the extra argument to test(), you don't need SFINAE, you can create a single test() method and all is simpler

    #include <iostream>
    
    using HashStruct = std::size_t;
    
    template<typename ... AA>
    using HashFunction = HashStruct & (*)(AA ... );
    
    HashStruct &  hf1 (std::size_t s)
     { static HashStruct val {0U}; return val = s; }
    
    HashStruct &  hf2 (std::size_t s, int i)
     { static HashStruct val {0U}; return val = s + std::size_t(i); }
    
    template <typename Tf, Tf F>
    class Collection;
    
    template <typename K, typename ... I, HashFunction<K, I...> F>
    class Collection<HashFunction<K, I...>, F>
     {
       public: 
          void test (I ... i)
           {
             K k=K();
    
             HashStruct & hh = F(k, i...);
    
             std::cout << hh << std::endl;
           }
     };
    
    int main ()
     {
       Collection<HashFunction<std::size_t>, hf1>       c1;
       Collection<HashFunction<std::size_t, int>, hf2>  c2;
    
       c1.test();    // print "0"
       c2.test(100); // print "100"
     }