4

The following code looks to me as though it should be working fine, but it produces

C2738: could not deduce template argument for 'Type'.

in VS2013.

template <typename ReturnType, typename...Args>
uint GetParameterSize(ReturnType(*method)(Args...))
{
    return ParameterSize<Args...>();
}

template <typename Type, typename...Remaining>
uint ParameterSize()
{
    uint output = sizeof(Type);

    if (sizeof...(Remaining) > 0)
    {
        output += ParameterSize<Remaining...>();
    }

    return output;
}

void MyMethod3(int a, char b, int c )
{
}

// Elsewhere
uint size = GetParameterSize(&MyMethod3);

I thought, "Oh maybe it's because it doesn't have a final condition." So I added:

template <typename Type>
uint ParameterSize()
{
    return sizeof(Type);
}

Which led to

C2668: 'ParameterSize' ambiguous call to overloaded function.

I mean, it looks simple enough and I think it should work. How can the compiler not deduce the arguments? I'm new to variadic templates so I might be missing something, but some help would be appreciated. Thanks!

6
  • I don't see any arguments to ParameterSize() that would allow it to deduce Type. Commented Dec 13, 2013 at 14:48
  • The types of the arguments of the function pointer provided will be the parameter pack provided to ParameterSize, no? Commented Dec 13, 2013 at 14:50
  • I think I get what you are trying to do. You want Args... to be int a, char b, int c. I would do a static_assert() in GetParameterSize to see if Args... is actually getting set. I would guess it might not be. Commented Dec 13, 2013 at 14:52
  • It is. sizeof...(Args) yields 3 in GetParameterSize. To see that, I had to comment out the recursive call to ParameterSize. Commented Dec 13, 2013 at 14:55
  • 1
    When Remaining is empty, you "call" (instantiate) in the first version ParameterSize<>() which doesn't specify Type, and Type cannot be deduced, hence the error. In the second version (with the overload), when you call ParameterSize<some_type>(), both overloads are viable and neither is more specialized; hence the ambiguity. Commented Dec 13, 2013 at 14:57

3 Answers 3

3
  if (sizeof...(Remaining) > 0)
    {
        output += ParameterSize<Remaining...>();
    }

The problem is that the above code is a run time check, not a compile time. So even though once Remaining is length 0, it still has to compile the signature for ParamaterSize<>(), even though it'll never be executed.

For this sort of problem, you can actually do everything at compile time.

template<typename ... Args>
struct ParameterSize;   
{
   static const uint value = 0;
};

template<typename Type, typename... Remain>
struct ParameterSize<Type, Remain...>
{
   static const uint value = sizeof(Type) + ParameterSize<Remain...>::value;
};

template <typename ReturnType, typename...Args>
uint GetParameterSize(ReturnType(*method)(Args...))
{
    return ParameterSize<Args...>::value;
}

Example at http://ideone.com/6aYRzI

Sign up to request clarification or add additional context in comments.

Comments

2

The simplest fix I can think of is to invoke overload resolution:

template <typename Type>
constexpr unsigned ParameterSize(int)
{
    return sizeof(Type);
}

template <typename Type, typename...Remaining>
constexpr unsigned ParameterSize(...)
{
    return sizeof(Type) + ParameterSize<Remaining...>(42);
}

template <typename ReturnType, typename...Args>
constexpr unsigned GetParameterSize(ReturnType(*method)(Args...))
{
    return ParameterSize<Args...>(42);
}

void MyMethod3(int a, char b, int c )
{
}

int main()
{
    // Elsewhere
    unsigned size = GetParameterSize(&MyMethod3);
}

The ellipsis makes the second overload a worse match than the first for arguments of type int (such as 42), that's how the ambiguity (ParameterSize<one_argument>(42)) is resolved.

2 Comments

With the constexpr, if you're using Visual Studio you'll need at least VS2013; otherwise just remove the constexpr.
You, sir, are good. I'm not a big fan of this, per-say. But it works for what I want without having to restructure things. This was just an example, the actual problem is a bit bigger than this but you've definitely gotten me where I need to be. Thank you.
1

There are other answers to why your code isn't working, so instead I'll give you an alternative. Since you seem to be accumulating sizeof(Type) to get the total stack size required to store function parameters, here's how I would do it:

    #include <iostream>
    #include <algorithm>

    template <class ... Args>
    unsigned ParameterSize()
    {
        unsigned sizes[sizeof...(Args)] = {sizeof(Args)...};
        return std::accumulate(std::begin(sizes), std::end(sizes), 0);
    }

    template <typename ReturnType, typename...Args>
    uint GetParameterSize(ReturnType(*method)(Args...))
    {
        return ParameterSize<Args...>();
    }

    void MyMethod(int,int,int){}

    int main()
    {
        std::cout << GetParameterSize(&MyMethod) << std::endl;
    }

Since you already had runtime code in your example, I'm assuming you don't mind some of the work being done at runtime.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.