Rambles around computer science

Diverting trains of thought, wasting precious time

Wed, 14 Oct 2009

C++ Gotme number 2: templates, typename and specialization syntax

Here's some innocuous-looking template definitions in C++.

// specialize this template!
template <unsigned spec_id> class Extended_By 
{
public:
    typedef struct {} spec;
};
        
// specialization for Dwarf 3
template <> class Extended_By<0U>
{
public:
    typedef empty_def spec;
};


template <unsigned spec_id> 
class table_def : public Extended_By<spec_id>::spec
{
public:
    typedef Extended_By<spec_id>::spec Extended;
// ...
};

I am implementing a delegation hierarchy of tables: lookup methods on the tables delegate to their base implementation if the lookup fails. The hierarchy is rooted at a special-cased “empty” definition (not shown). Each non-root table has a similar implementation, but must be a separate class (as it may want to override certain member functions), hence my use of templates. To define a new table, we specialize the latter template with a new integer argument denoting the concrete table being defined. (In case you're wondering, tables are singletons---but they get pointed-to at runtime, hence virtual dispatch.) The Extended_By template is simply a compile-time mapping from integers (which denote tables) to the type of the underlying table (i.e. the table which that table delegates to, if its lookup fails).

Unfortunately, the code above doesn't compile.

g++ -Wall -O0 -g3 -I/home/srk31/opt/include -I/usr/include/python2.5 -I/usr/incl
ude -I../../../libsrk31c++ -c -o "spec.o" "spec.cpp"
In file included from spec.cpp:8:
spec.hpp:242: error: type `dwarf::spec::Extended_By<spec_id>' is not derived fro
m type `dwarf::spec::table_def<spec_id>'

The error message makes no sense at all. In fact, the problem is that the compiler can't tell that Extended_By<spec_id> is a type. If you insert the typename keyword into the latter template definition, as follows....

template <unsigned spec_id> 
class table_def : public Extended_By<spec_id>::spec
{
public:
    typedef typename Extended_By<spec_id>::spec Extended;
// ...
};

...then it all magically starts working. I must confess I'm not entirely sure why---surely the usual name lookup on Extended_By would work just fine?

On a similar theme, I was specializing some predicates defined by the table template, using code as follows.

template<unsigned spec_id> 
bool table_def<0U>::attr_describes_location(int attr) const
{
	return this->Extended::attr_describes_location(attr)
		|| attr == DW_AT_location
		|| attr == DW_AT_data_member_location
		|| attr == DW_AT_vtable_elem_location
		|| attr == DW_AT_string_length
		|| attr == DW_AT_use_location
		|| attr == DW_AT_return_addr;
}

This also fails to compile, with a very confusing message.

g++ -Wall -O0 -g3 -I/home/srk31/opt/include -I/usr/include/python2.5 -I/usr
/include -I../../../libsrk31c++ -c -o "spec.o" "spec.cpp"
spec.cpp:272: error: prototype for `bool dwarf::spec::table_def<0u>::attr_d
escribes_location(int) const' does not match any in class `dwarf::spec::tab
le_def<0u>'
spec.hpp:278: error: candidate is: bool dwarf::spec::table_def::at
tr_describes_location(int) const [with unsigned int spec_id = 0u]
make: *** [spec.o] Error 1
The solution is to eliminate the template parameter.

template<> 
bool table_def<0U>::attr_describes_location(int attr) const
{
	return this->Extended::attr_describes_location(attr)
		|| attr == DW_AT_location
		|| attr == DW_AT_data_member_location
		|| attr == DW_AT_vtable_elem_location
		|| attr == DW_AT_string_length
		|| attr == DW_AT_use_location
		|| attr == DW_AT_return_addr;
}

It's not really clear why the latter is a valid syntax for specialization while the former isn't. However, a useful rule of thumb would seem to be that you should only list template arguments up-front (after “template”) if your definition depends on them. It doesn't matter if the originating class template uses more arguments. By contrast, the argument list attached to the classname is what nails down the particular template instance you're specializing for---this argument list should correspond directly to that of the template class definition.

[/devel] permanent link contact


Powered by blosxom

validate this page