Ajay Ajay - 26 days ago 9
C++ Question

Call function based on template argument type

There are two "C" functions:

void fooA(const char*);
void fooW(const wchar_t*);


Then there is a wrapper template function:

template<typename _TChar>
void foo(const _TChar* str)
{
// call fooA or fooB based on actual type of _TChar
// std::conditional .. ?
// fooA(str);
// fooW(str);
}


If the caller calls
foo("Abc")
, this template function should make a compile-time call to
fooA
. Similiarly,
foo(L"Abc")
should make the final call to
fooW
.

How do I do that? I thought of using
std::conditional
but couldn't make it.

I cannot make
fooA
or
fooB
overloaded, since these are C functions.

Answer

You can put all your wchar_t versions in a class template, say overloads and their char counter-parts in its specialization as shown below:

template<typename WideCharVersion> 
struct overloads
{
    void foo(wchar_t const * arg)
    {
       FooW(arg);
    }
    //more wchar_t functions
};

template<> 
struct overloads<std::false_type>
{
    void foo(char const * arg)
    {
       FooA(arg);
    }
    //more char functions
};

//a friendly alias!
template<typename T>
using is_wide_char = typename std::is_same<whar_t, T>::type;

And then you can use them as:

template<typename _TChar>
void foo(const _TChar* str)
{
    overloads<is_wide_char<_TChar>>::foo(str);
}

Expression SFINAE makes it easy!

Other way is to use Expression SFINAE which does not require to you write anything like overloads and does the same job with less code:

template<typename _TChar>
void foo(const _TChar* str)
{
    invokeOne(fooA, fooW, str);
}

And then you can implement invokeOne as:

 template<typename F1, typename F2, typename ... Args>
 auto invokeOne(F1 f1, F2 f2, Args && ... args) -> decltype(f1(args...))
 {
     return f1(args...);
 }

 template<typename F1, typename F2, typename ... Args>
 auto invokeOne(F1 f1, F2 f2, Args && ... args) -> decltype(f2(args...))
 {
     return f2(args...);
 }

Have a look at the online demo.

In this approach, you don't have to add the overloads to the overloads class template and to its specialization. Instead you just pass them as arguments to invokeOne which calls the right overload for you.

Hope that helps.