calling generic template function from template specialization

J

johanatan

I have two functions that are nearly identical (one validates a row
and one validates a table). Since a table is open-ended (i.e., can
have infinite rows) but a row is finite, there is one addtional line
of code in the ValidateRow func which asserts that the row data has
the appropriate number of cells.

I would like to make these a template function with a specialization
for the row that asserts the one extra condition and then calls the
generic template function. Is this possible?

Thanks,
Jonathan
 
J

Jeff Schwab

johanatan wrote:
johanatan said:
I have two functions that are nearly identical (one validates a row
and one validates a table). Since a table is open-ended (i.e., can
have infinite rows) but a row is finite, there is one addtional line
of code in the ValidateRow func which asserts that the row data has
the appropriate number of cells.

I would like to make these a template function with a specialization
for the row that asserts the one extra condition and then calls the
generic template function. Is this possible?

Thanks,
Jonathan
I have two functions that are nearly identical (one validates a row
and one validates a table). Since a table is open-ended (i.e., can
have infinite rows) but a row is finite, there is one addtional line
of code in the ValidateRow func which asserts that the row data has
the appropriate number of cells.

I would like to make these a template function with a specialization
for the row that asserts the one extra condition and then calls the
generic template function. Is this possible?

Yes, but you probably want an overload rather than a specialization.

It sounds like the code to validate a row is identical (except for the
assertion) to the code that validates a table. Is this correct? If so,
you can do something like:

#include <cassert>
#include <cstdlib>
#include <string>
#include <vector>

std::size_t const row_size = 42;

typedef std::string cell_type;
typedef std::vector<cell_type> row_type;
typedef std::vector<row_type> table_type;

/* Works for tables and rows. */
template<typename T>
void validate(T const& t) {
// ...
}

void validate(row_type const& row) {
assert(row.size() == row_size);
validate<row_type>(row);
}

int main() {
row_type row(row_size );
table_type table;

table.push_back(row);

/* Invokes an instantiation of the template. */
validate(table);

/* Invokes the row-specific overload, then
* an instantiation of the template. */
validate(row);
}


If you know the row's size at compile time, you might want to replace
the row class with a template, so that the size can be a template parameter.
 
J

johanatan

johanatan wrote:





Yes, but you probably want an overload rather than a specialization.

It sounds like the code to validate a row is identical (except for the
assertion) to the code that validates a table.  Is this correct?  If so,
you can do something like:

     #include <cassert>
     #include <cstdlib>
     #include <string>
     #include <vector>

     std::size_t const row_size = 42;

     typedef std::string cell_type;
     typedef std::vector<cell_type> row_type;
     typedef std::vector<row_type> table_type;

     /* Works for tables and rows. */
     template<typename T>
     void validate(T const& t) {
         // ...
     }

     void validate(row_type const& row) {
         assert(row.size() == row_size);
         validate<row_type>(row);
     }

     int main() {
         row_type row(row_size );
         table_type table;

         table.push_back(row);

         /* Invokes an instantiation of the template. */
         validate(table);

         /* Invokes the row-specific overload, then
          * an instantiation of the template. */
         validate(row);
     }

If you know the row's size at compile time, you might want to replace
the row class with a template, so that the size can be a template parameter.- Hide quoted text -

- Show quoted text -

Ahh. Hadn't thought of mixing overloads and templates. I was
thinking that the validate(row_type) would have to also be a template
(specialization).

As for the size template param-- won't be necessary as both validates
are members of a class that already knows the row size.

Thanks!
--Jonathan
 
J

johanatan

johanatan wrote:





Yes, but you probably want an overload rather than a specialization.

It sounds like the code to validate a row is identical (except for the
assertion) to the code that validates a table.  Is this correct?  If so,
you can do something like:

     #include <cassert>
     #include <cstdlib>
     #include <string>
     #include <vector>

     std::size_t const row_size = 42;

     typedef std::string cell_type;
     typedef std::vector<cell_type> row_type;
     typedef std::vector<row_type> table_type;

     /* Works for tables and rows. */
     template<typename T>
     void validate(T const& t) {
         // ...
     }

     void validate(row_type const& row) {
         assert(row.size() == row_size);
         validate<row_type>(row);
     }

     int main() {
         row_type row(row_size );
         table_type table;

         table.push_back(row);

         /* Invokes an instantiation of the template. */
         validate(table);

         /* Invokes the row-specific overload, then
          * an instantiation of the template. */
         validate(row);
     }

If you know the row's size at compile time, you might want to replace
the row class with a template, so that the size can be a template parameter.

Though it isn't required in this case, just out of curiosity, is it
possible to do what I originally asked?
 
J

johanatan

If you know the row's size at compile time, you might want to replace
the row class with a template, so that the size can be a template parameter.

And actually, yea, it would be best to move some of the metadata to
compile time rather than run time. Thanks for the suggestion.
 
J

Jeff Schwab

johanatan said:
Though it isn't required in this case, just out of curiosity, is it
possible to do what I originally asked?

Sort of. You still need two version of the function (one that asserts,
and another that doesn't); once you've specialized the template, though,
there's no way to un-specialize it. One work-around is to have an
"inner" (implementation) template invoked by the outer (interface) template.

template<typename T>
void validate_impl(T const& t) {
// ...
}

/* Works for tables and rows. */
template<typename T>
void validate(T const& t) {
validate_impl(t);
}

template<>
void validate(row_type const& row) {
assert(row.size() == row_size);
validate_impl(row);
}
 
J

James Kanze

I have two functions that are nearly identical (one validates
a row and one validates a table). Since a table is open-ended
(i.e., can have infinite rows) but a row is finite, there is
one addtional line of code in the ValidateRow func which
asserts that the row data has the appropriate number of cells.
I would like to make these a template function with a
specialization for the row that asserts the one extra
condition and then calls the generic template function. Is
this possible?

The usual solution would be to use traits. Something like:

template< typename T >
struct AdditionalValidations
{
static bool isOk( T const& ) { return true ; }
} ;

template<>
struct AdditionalValidations< Row >
{
static bool isOk( Row const& row )
{
return row.size() == whatever ;
}
} ;

In your template function, you would add code like:

if ( theUsualValidations( obj )
&& AdditionalValidations< T >::isOk( obj ) {
// error handling...
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top