Template typedef as function parameter

Jul 9, 2010 at 12:25pm
Hi board!

I have a situation where my (class') function takes a template class parameter, but I'd like to have that parameter type typedef'd.

I have an engine that is used to relay variables. The variables can be of POD type, struct or my own SharedVar<T>.

Here's some code to enlighten the problem:
1
2
3
4
5
6
7
8
9
10
11
template <class PlRepo>
class Engine : public PlRepo
{
public:
	// Let's call this GetVar(1)
	template <class T>
	int GetVar(const std::string &Name, T &Var);

	// And this GetVar(2)
	template <class T>
	int GetVar(const std::string &Name, SharedVar<T> &Var);


PlRepo here is the repository class that archives SharedVar<T>'s. Let's not mind about it here, except that is needs to typedef what class it is storing:

1
2
3
template <class T> struct VarType {
	typedef SharedVar<T> Type;
};


The example above works, but I'd want to rid GetVar function call of SharedVar<T> and replace it with repository's typedef:
1
2
	template <class T>
	int GetVar(const std::string &Name, typename PlRepo::VarType<T>::Type &Var);


Compiler does not complain about this:
1
2
	SharedVar<std::string> Str2;
	Engi.GetVar("Str", Str2);


But the problem is that this call goes to the GetVar(1) and not GetVar(2). The call works as intended until I replace SharedVar<T> & with typename PlRepo::VarType<T>::Type.

Any grand ideas why this might be happening?
Jul 9, 2010 at 1:04pm
As far as I can tell:
1
2
SharedVar<std::string> Str2;
Engi.GetVar("Str", Str2);


does not match:
1
2
template <class T>
int GetVar(const std::string &Name, typename PlRepo::VarType<T>::Type &Var);


If you make a simple example without your engine class or PlRepo scope, and just look at template functions, the compilers cannot match. This is not what I would expect, and at this point, I don't know why (either).
Last edited on Jul 9, 2010 at 1:04pm
Jul 9, 2010 at 8:12pm
I read somewheer that function parameters of the format template_class<T>::sub_type - are not considered as candidates for template parameter deduction.
Jul 9, 2010 at 8:28pm
That is true; GetVar cannot work as declared.
Jul 9, 2010 at 8:39pm
Does this work?

1
2
template <class T, template <class A> class PlRepo::VarType<A> >
int GetVar(const std::string &Name, typename PlRepo::VarType<T>::Type &Var);

EDIT: Filled the class keyword I forgot...
Last edited on Jul 9, 2010 at 8:44pm
Jul 10, 2010 at 9:12am
Thanks for all the replies!

@m4ster r0shi
I will test your suggestion on Monday and will let the thread know how it went.
Jul 12, 2010 at 7:23am
@roshi
Your suggestion made the compiler nag, I'm afraid. I didn't quite understand what kind of solution you were after, so I didn't know how to fix it (My own fault, without a doubt) :)

The compiler said:
share_engine.hpp:54: error: declaration of ‘template<class PlRepo> template<class A> class PlRepo’
share_engine.hpp:39: error:  shadows template parm ‘class PlRepo’

Line 54 is the GetVar function declaration and line 39 is the Engine class template declaration.

I experimented a little further with the original code trying this:
1
2
template <class PlRepo> template <class T>
int Engine<PlRepo>::GetVar(const std::string &Name, typename PlRepo::template VarType<T>::Type &Var)

The compiler's hint made me add that "template" keyword between "PlRepo::" and "VarType<T>". I must admit I've never seen that kind of syntax.
Found an explanation here: http://publib.boulder.ibm.com/infocenter/macxhelp/v6v81/index.jsp?topic=/com.ibm.vacpp6m.doc/language/ref/clrc16explicit_spec_members.htm
If the name of a member template specialization appears after a ., ->, or :: operator, and that name has explicitly qualified template parameters, prefix the member template name with the keyword template.


Anyway, now the error message is:
test.cpp:85: error: no matching function for call to ‘Engine<SharedRepo>::GetVar(std::string&,
SharedVar<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)’

I got an idea to test the VarType like this:
1
2
3
	SharedRepo::VarType<std::string>::Type Str2;
	std::string Name("aa");
	Engi.GetVar(Name, Str2);


It turns out that the VarType<>::Type -declaration works and creates really a SharedVar<T> object (made sure by adding print in SharedVar's constructor). The call to Engi.GetVar() gives the same compiler error as mentioned before.

I'm quite baffled by this.

EDIT: Formatted lines a bit.

EDIT 2: Found an explanation to the funny template keyword in the first code snippet.
Last edited on Jul 12, 2010 at 7:55am
Jul 12, 2010 at 12:02pm
You are fighting an uphill battle. guestgulkan had it right to begin with. C++ does not allow you
to do things like:

1
2
3
4
5
template< typename Type >
struct AType {
    template <class T>
    void func( typename Type::ContainedType<T>::AnotherType foo );
};


ie, you cannot use template paramters to "look inside" other types like that.
Jul 12, 2010 at 12:32pm
I refuse to give up to plain syntax limitations and try to find a workaround for this :)

I'll die of old age before the standard committee fixes C++ ;)
Jul 12, 2010 at 1:18pm
personally< i would have gone for one of two easier options

1. Explicitly supplying the type to the function:
Like:
GetVar<int>(some_string, some_variable_of_SharedVar<int>::type )


2. Change the function signature to add a dummy unused variable of type T
So the function signature would look like this
1
2
template <class T>
int GetVar(const std::string &Name, typename PlRepo::VarType<T>::Type &Var, const T&);

and call it like
GetVar(some_string, some_variable_of_SharedVar<int>::type, int() )
Last edited on Jul 12, 2010 at 1:20pm
Jul 12, 2010 at 1:31pm
What about something like (kinda kludgy, but maybe it'll get you past this hurdle so you can dedicate
your life to something more meaningful :) :

1
2
3
4
5
6
7
template< typename T >
struct Foo {
    typedef PlRepo::VarType<T>::Type type;
};

template< class T >
int GetVar( const std::string&, Foo& var, const T& );


Jul 12, 2010 at 8:07pm
(kinda kludgy, but maybe it'll get you past this hurdle so you can dedicate
your life to something more meaningful :)

I'd totally agree if this was not for learning and fun :)

Gentlemen and dear ladies! May I present you the solution.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class SharedVarRepo
{
	typedef SharedVarBase VarBaseType;  // All SharedVar<T>'s are derived from this.
	...
};

template <class PlRepo = SharedVarRepo>  // Default repository is SharedVarRepo
class Engine : public PlRepo
{
protected:
	// For any type that is derived from PlRepo::VarBaseType, IsRepoVar = true
	// all the other types get IsRepoVar = false
	struct EmptyType {};
	template <class T, bool IsRepoVar = std::tr1::is_base_of<typename PlRepo::VarBaseType, T>::value> struct Assist;
	
	// Specialisation for Assist<int>, Assist<MyClass>, ...
	template <class T> struct Assist<T, false>
	{
		typedef EmptyType SubType;
	};
	
	// Specialisation for Assist<SharedVar<int> >, Assist<SharedVar<float> >, Assist<SharedVarBase *>, ...
	template <class T> struct Assist<T, true>
	{
		typedef typename T::Type SubType;
	};

public:
	template <class T>
	int GetVar(const std::string &Name, T &Var)
	{ return GetVar(Name, Var, typename Assist<T>::SubType() ); }
	
protected:
	// GetVar calls with the SharedVarBase derivatives go here
	template <class T, class U>
	int GetVar(const std::string &Name, T &Var, U const &Dummy);

	// GetVar calls with any other type go here
	template <class T>
	int GetVar(const std::string &Name, T &Var, EmptyType const &Dummy);


Thanks to everybody for your comments and grand ideas! Now I'll go and float a moment in my coder's high ;)

Any further comments and remarks are of course very welcome.

EDIT: Cleaned up the code a bit.
Last edited on Jul 13, 2010 at 5:58am
Topic archived. No new replies allowed.