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::string
s.
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...);
}