-1

With MSVC12, I get a strange syntax error when trying to typedef a dependent typename like so:

template <typename ... LeftT>
struct A{
    template <typename ...>
    struct B{};

    template <typename T, typename ... RightT>
    struct B <T, RightT ... >{
        typedef typename A<LeftT ..., T> nextA;
        typedef typename nextA::B<RightT ...> nextB; //error C2059: syntax error : '<'
    };
};

This is everything there's to it, the template is never instantiated except inside itself.

My question: Why am I getting a syntax error here? Is this not valid syntax?

You question (probably): Why are you even doing this?

The basic idea is forwarding with type conversion. This would be done by specialising B so that T is of a certain type, which can then be converted. How that works in detail is explained below, including the complete code snippet.

If this question is badly worded or hard to understand, please help me improve it. This is one of the most complex template problems I've been working on English is not my native language.


Why the madness?

As a bit of an exercise, I want to write a template construct that allows me to call printf and similar functions that require char[]s with std::strings.

For that, I have to iterate through every single argument, and specialise for the case that it may be a std::string. In order to do that, I have a struct fwd<typename func_t, typename ... LeftArgs>, which contains all arguments that have been processed, and inside that a struct one_arg<typename Arg, typename ... RightArgs> that allows the processing of the current argument by being specialised for the empty base case and the case that Arg is std::string. The base case (no arguments, or none left to process), is handled like this:

template<typename ...>
struct one_arg
{
    //implementation of return_type will be shown below
    static return_type forward(func_t func, LeftArgs ... leftArgs){
        return func(leftArgs ...); //execute function
    }
};

Specialising the base case is the common case that an argument has any type except std::string:

template<typename Arg, typename ... RightArgs>
struct one_arg < Arg, RightArgs ...>
{
    //move Arg to the processed arguments
    typedef typename fwd< func_t, LeftArgs..., Arg>::one_arg<RightArgs...> next_arg;

    static return_type forward(func_t func, LeftArgs ... leftArgs, Arg arg, RightArgs ... rightArgs){
        return next_arg::forward(func, leftArgs ..., arg, rightArgs ...);
    }
};

Specialising this common case even further, with nearly identical syntax, is the case that the current argument is indeed of type std::string:

template<typename ... RightArgs>
struct one_arg < std::string, RightArgs ...> 
{
    //again, move Arg to the processed arguments, but as a char[]
    typedef typename fwd< func_t, LeftArgs..., char[]>::one_arg<RightArgs...> next_arg;

    static return_type forward(func_t func, LeftArgs ... leftArgs, std::string arg, RightArgs ... rightArgs){
        //convert string to char[]
        return next_arg::forward(func, leftArgs ..., arg.c_str(), rightArgs ...);
    }
};

I hope the structure was obvious. If not, here is the whole snippet, ready to go.

Complete snippet:

#include <string>

using namespace std;

//get the return type of a function
template <typename T>
struct get_return_type;
template <typename R, typename ... A>
struct get_return_type<R(A...,...)>
{
    typedef R type;
};

template<typename func_t, typename ... LeftArgs>
struct fwd{
    typedef typename get_return_type<func_t> return_type;

    //empty base case
    template<typename ...>
    struct one_arg
    {
        static return_type forward(func_t func, LeftArgs ... leftArgs){
            return func(leftArgs ...);
        }
    };

    //normal forwarding
    template<typename Arg, typename ... RightArgs>
    struct one_arg < Arg, RightArgs ...>
    {
        typedef typename fwd< func_t, LeftArgs..., Arg>::one_arg<RightArgs...> next_arg;

        static get_return_type<func_t> forward(func_t func, LeftArgs ... leftArgs, Arg arg, RightArgs ... rightArgs){
            return next_arg::forward(func, leftArgs ..., arg, rightArgs ...);
        }
    };

    //specialisation for std::string
    template<typename ... RightArgs>
    struct one_arg < std::string, RightArgs ...> 
    {
        typedef typename fwd< func_t, LeftArgs..., char[]>::one_arg<RightArgs...> next_arg;

        static get_return_type<func_t> forward(func_t func, LeftArgs ... leftArgs, std::string arg, RightArgs ... rightArgs){
            return next_arg::forward(func, leftArgs ..., arg.c_str(), rightArgs ...);
        }
    };
};

template<typename func_t, typename ... Args>
typename fwd<func_t>::one_arg<Args ...>::return_type forward_stoc(func_t func, Args ... args){
    typedef typename fwd<func_t>::one_arg<Args ...> next_arg;
    return next_arg::forward(func, args...);
}
iFreilicht
  • 9,961
  • 6
  • 32
  • 63
  • I did not know that there was a missing keyword, so I don't think this is a duplicate, rather than a related question. – iFreilicht Oct 07 '14 at 01:38
  • In a conforming compiler, the first one shouldn't have `typename`, the second one needs a `template`. I have no idea about MSVC, though. – T.C. Oct 07 '14 at 01:38
  • @T.C. That is correct, the first one doesn't need `typename`. I did not know that the `template` keyword could and need be used that way. – iFreilicht Oct 07 '14 at 01:39
  • @iFreilicht: That question explains exactly where and why you need these keywords, so does answer this question (and questions involving dependent scopes in general). I've also given a simple answer to your specific question. – Mike Seymour Oct 07 '14 at 01:44
  • @MikeSeymour that is true, and I am thankful for your answer, but still, my question has not been asked before in my eyes. – iFreilicht Oct 07 '14 at 01:47

1 Answers1

2

nextA is a dependent scope, so you need to indicate that B is the name of a template:

typedef typename nextA::template B<RightT ...> nextB;
                        ^^^^^^^^

Note that there shouldn't be a typename in the declaration of nextA, since there's no dependent scope involved:

typedef A<LeftT ..., T> nextA;

For the gory details, see Where and why do I have to put the "template" and "typename" keywords?

Community
  • 1
  • 1
Mike Seymour
  • 235,407
  • 25
  • 414
  • 617