Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle void decltype();

I'd like to create a template that calls a member function on another object that returns the same type as the member function. The syntax for using decltype on the member function is a bit ugly, but it seems to work in every case except one.

Here's the call:

struct container: map<string, C> 
{
    template< typename MemFnPtrType, typename... _ArgTypes>
    auto safeOperation(string key, MemFnPtrType mfp, _ArgTypes&&... args )
        -> decltype( (((C*)nullptr)->*mfp)(args...))
    {
        C* pC = NULL;
        decltype((pC->*mfp)(args...)) result;

        iterator it = find(key);
        if (it != end())
        {
            C* pC = &(it->second);
            result = (pC->*mfp)(args...);
            cout << "result:" << result << "\n";
        }
        else
        {
            cout << "key: " << key << " missing\n";
        }
        return result;
    }
};

This works fine until the member function returns void.

Is there a way to detect this and leave out the offending lines?

I could obviously create a voidSafeOperation function. I don't mind creating another template, but I'd like to use the same name "safeOperation" so the call sites don't need to use different helpers based on the return type of the member function.

Thanks!

Full Example: http://cpp.sh/7ft

like image 803
Brad Avatar asked Jan 09 '23 08:01

Brad


1 Answers

Unfortunately, I think you're stuck here with having to SFINAE on the return type. Start with a type trait (this is way cleaner than your decltype expression)

template <typename MF, typename... Args>
using Res = typename std::result_of<MF(C, Args...)>::type;

And then just switch:

template <typename MF, typename... Args>
typename std::enable_if<
    std::is_same<Res<MF, Args...>, void>::value
>::type safeOperation(string key, MF mfp, Args... args)
{
     /* void case */
}

template <typename MF, typename... Args>
typename std::enable_if<
    !std::is_same<Res<MF, Args...>, void>::value,
    Res<MF, Args...>
>::type safeOperation(string key, MF mfp, Args... args)
{
     /* non-void case */
}

Or you could tag dispatch on is_void:

template <typename MF, typename... Args>
Res<MF, Args...> safeOperation(string key, MF mfp, Args... args)
{
    return safeOperation(std::is_void<Res<MF, Args...>>{},
                         key, mfp, args...);
}

with:

template <typename MF, typename... Args>
void safeOperation(std::true_type /* void */, 
                   string key, MF mfp, Args... args) 
{ .. }

template <typename MF, typename... Args>
Res<MF, Args...> safeOperation(std::false_type /* non-void */, 
                               string key, MF mfp, Args... args) 
{ .. }
like image 78
Barry Avatar answered Jan 10 '23 22:01

Barry