By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,562 Members | 1,244 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,562 IT Pros & Developers. It's quick & easy.

Template function specialization/overloading

P: n/a
I am facing the following scenario: I have a class 'A', that implements
some concept C -- but we know this, not because A inherits from a
virtual class 'C', but only because a trait tell us so:

class A {};

template <typename T>
struct Is_C
{
static const bool value = false;
};

template <>
struct Is_C<A>
{
static const bool value = true;
};
What I would like to do is, given an instance 'a' of A, call a function
'foo(a)' that would have specific code for class A -- and have default
code for other classes.

The following

template < typename T >
void foo(T t)
{
if (Is_C<T>::value)
{
/* ... */
}
else
{
/* ... */
}
}

would work, but then, it is not extensible to another trait 'Is_D'
unless I modify the function foo. Ideally I would have a function foo
for the general case (corresponding to the 'else') and as many
functions foo for all the different tests, but I am not sure how to do
that. I would like to use the boost::enable_if, but if I use function
overloading, I need to have a disable_if in my 'general case' foo for
each 'specialized' foo:

// General case
template < typename T >
typename disable_if<Is_C<T> >::type // This has to be modified if
another foo is added
foo(T t)
{
/* ... */
}

// Specialization for concept C
template < typename T >
typename enable_if<Is_C<T> >::type
foo(T t)
{
/* ... */
}

I would really appreciate any suggestion for this problem.

Thanks

B.

Jul 23 '05 #1
Share this Question
Share on Google+
11 Replies


P: n/a
ga*******@voila.fr wrote:
I am facing the following scenario: I have a class 'A', that implements
some concept C -- but we know this, not because A inherits from a
virtual class 'C', but only because a trait tell us so:

class A {};

template <typename T>
struct Is_C
{
static const bool value = false;
};

template <>
struct Is_C<A>
{
static const bool value = true;
};
What I would like to do is, given an instance 'a' of A, call a function
'foo(a)' that would have specific code for class A -- and have default
code for other classes.

The following

template < typename T >
void foo(T t)
{
if (Is_C<T>::value)
{
/* ... */
}
else
{
/* ... */
}
}

would work, but then, it is not extensible to another trait 'Is_D'
unless I modify the function foo. Ideally I would have a function foo
for the general case (corresponding to the 'else') and as many
functions foo for all the different tests, but I am not sure how to do
that. I would like to use the boost::enable_if, but if I use function
overloading, I need to have a disable_if in my 'general case' foo for
each 'specialized' foo:

// General case
template < typename T >
typename disable_if<Is_C<T> >::type // This has to be modified if
another foo is added
foo(T t)
{
/* ... */
}

// Specialization for concept C
template < typename T >
typename enable_if<Is_C<T> >::type
foo(T t)
{
/* ... */
}

I would really appreciate any suggestion for this problem.
Interesting problem. Of course, the first think I want to say is that
you're confusing yourself and others calling your "traits" 'Is_C' or
'Is_D'. Apparently your class A _isn't_ C or D, otherwise inheritance
should work just fine. You should name your concept as it is, something
like 'implements_C' or 'implements_D'.

Anyway... I thought of providing the helper for your 'foo':
------------------------------------------------------------------------
#include <iostream>

template<class T> struct implements_C { enum { value = 0 }; };

template<class T, bool b> struct foo_helper {
static void foo(T t)
{
std::cout << "Generic foo\n";
}
};

template<class T> struct foo_helper<T, true> {
static void foo(T t)
{
std::cout << "'true'-specific foo\n";
}
};

template<class T> void foo(T t)
{
foo_helper<T, implements_C<T>::value>::foo(t);
}

class A {};
class B {};

template<> struct implements_C<A> { enum { value = 1 }; };

int main()
{
A a;
foo(a);
B b;
foo(b);
}
------------------------------------------------------------------------

Now, if you need to add another "policy", the 'foo_helper', which does
the actual work, shouldn't change. What you change is the 'foo':
template<class T> void foo(T t)
{
foo_helper<T,
implements_C<T>::value || implements_D<T>::value::foo(t);

}

Here is amended code with two policies:
------------------------------------------------------------------------
#include <iostream>

template<class T> struct implements_C { enum { value = 0 }; };
template<class T> struct implements_D { enum { value = 0 }; };

template<class T, bool b> struct foo_helper {
static void foo(T t)
{
std::cout << "Generic foo\n";
}
};

template<class T> struct foo_helper<T, true> {
static void foo(T t)
{
std::cout << "'true'-specific foo\n";
}
};

template<class T> void foo(T t)
{
foo_helper<T, implements_C<T>::value || implements_D<T>::value >::foo(t);
}

class A {};
class B {};
class AB {};
class C {};

template<> struct implements_C<A> { enum { value = 1 }; };
template<> struct implements_D<B> { enum { value = 1 }; };
template<> struct implements_C<AB> { enum { value = 1 }; };
template<> struct implements_D<AB> { enum { value = 1 }; };

int main()
{
A a;
foo(a);
B b;
foo(b);
AB ab;
foo(ab);
C c;
foo(c);
}
--------------------------------------------------------------------

HTH

V
Jul 23 '05 #2

P: n/a
Thanks for your answer. However, my problem is slightly different --
let me explain further. I want to leave the possibility open to add a
third function foo in case somebody comes up with another concept D for
which foo should be implemented differently. E.g:

// General case
template < typename T >
// the following line has been modify when third foo was added
typename disable_if<implements_C<T> || implements_D<T> >::type
foo(T t)
{
/* ... */
}

// Specialization for concept C
template < typename T >
typename enable_if<implements_C<T> >::type
foo(T t)
{
/* ... */
}

// Specialization for concept D
template < typename T >
typename enable_if<implements_D<T> >::type
foo(T t)
{
/* ... */
}
One could generalize your idea of helper functions by replacing a bool
by an integer, but I don't like the idea of having to modify the helper
function -- in that sense, I am not sure what benefits come from you
approach compared to the previous collection of functions. Ideally
somebody should be able to add a 'foo' without having to modify the
remaining of the code.

Jul 23 '05 #3

P: n/a
ga*******@voila.fr wrote:
Thanks for your answer. However, my problem is slightly different --
let me explain further. I want to leave the possibility open to add a
third function foo in case somebody comes up with another concept D for
which foo should be implemented differently.

What you need could be achieved by adding a 'tag' inside your traits
class. For example, here's some pseudocode:

struct ATag { };
struct BTag { };
struct CTag { };

template <typename TYPE>
struct Traits;

class SomeClassThatHasATrait { ... };
class SomeClassThatHasBTrait { ... };
class SomeClassThatHasCTrait { ... };

template <>
struct Traits<SomeClassThatHasATrait> { typedef ATag Tag; };

template <>
struct Traits<SomeClassThatHasBTrait> { typedef BTag Tag; };

template <>
struct Traits<SomeClassThatHasCTrait> { typedef CTag Tag; };

template <typename TYPE>
void foo(TYPE object, ATag)
{
... code for types that have A trait ....
}

template <typename TYPE>
void foo(TYPE object, BTag)
{
... code for types that have B trait ....
}

template <typename TYPE>
void foo(TYPE object, CTag)
{
... code for types that have C trait ....
}

template <typename TYPE>
void foo(TYPE object)
{
typedef typename Traits<TYPE>::Tag Tag;
foo(object, Tag());
}

-----------------------------------------

Now, when you want to add a new 'D' trait, all you have to do is define
a tag for it:

struct DTag { };

Define some class that has the D trait:

class SomeClassThatHasDTrait { ... };

template <>
struct Traits<SomeClassThatHasDTrait> { typedef DTag Tag; };

Next, define a foo function for classes that have the D trait:

template <typename TYPE>
void foo(TYPE object, DTag)
{
... code for types that have C trait ....
}
Hope this helps,
-shez-

Jul 23 '05 #4

P: n/a
Shezan Baig wrote:
ga*******@voila.fr wrote:
Thanks for your answer. However, my problem is slightly different --
let me explain further. I want to leave the possibility open to add a
third function foo in case somebody comes up with another concept D for
which foo should be implemented differently.
What you need could be achieved by adding a 'tag' inside your traits
class. For example, here's some pseudocode:

struct ATag { };
struct BTag { };
struct CTag { };

template <typename TYPE>
struct Traits;


This has to be somehow defined for "any other type TYPE".

class SomeClassThatHasATrait { ... };
class SomeClassThatHasBTrait { ... };
class SomeClassThatHasCTrait { ... };
[...]

template <typename TYPE>
void foo(TYPE object)
{
typedef typename Traits<TYPE>::Tag Tag;
And it's not going to compile if there is no 'Tag' defined. You'll
need to have some kind of "default traits" and the corresponding
"default" behaviour.
foo(object, Tag());
}


V
Jul 23 '05 #5

P: n/a


Victor Bazarov wrote:
Shezan Baig wrote:
ga*******@voila.fr wrote:
Thanks for your answer. However, my problem is slightly different --
let me explain further. I want to leave the possibility open to add a
third function foo in case somebody comes up with another concept D for
which foo should be implemented differently.


What you need could be achieved by adding a 'tag' inside your traits
class. For example, here's some pseudocode:

struct ATag { };
struct BTag { };
struct CTag { };

template <typename TYPE>
struct Traits;


This has to be somehow defined for "any other type TYPE".

class SomeClassThatHasATrait { ... };
class SomeClassThatHasBTrait { ... };
class SomeClassThatHasCTrait { ... };
[...]

template <typename TYPE>
void foo(TYPE object)
{
typedef typename Traits<TYPE>::Tag Tag;


And it's not going to compile if there is no 'Tag' defined. You'll
need to have some kind of "default traits" and the corresponding
"default" behaviour.


Yeap. If you need default behaviour, then instead of:

template <typename TYPE>
struct Traits;

you need to do:

template <typename TYPE>
struct Traits {
typedef DefaultTag Tag;
};

and create a corresponding foo(TYPE, DefaultTag) function.

-shez-

Jul 23 '05 #6

P: n/a
This is indeed in the spirit of what I was looking for. It is still one
footstep of being exactly what I need though, because it doesn't reuse
the information that was already there in the implements_X classes. In
the first place I thought that this problem is hard precisely because
the information is not encoded in the form of class types -- and your
solution is exactly going manually from one form to the other. Is there
a way to go without having to rewrite all these traits specialization?

B.

Jul 23 '05 #7

P: n/a
ga*******@voila.fr wrote:
This is indeed in the spirit of what I was looking for. It is still one
footstep of being exactly what I need though, because it doesn't reuse
the information that was already there in the implements_X classes. In
the first place I thought that this problem is hard precisely because
the information is not encoded in the form of class types -- and your
solution is exactly going manually from one form to the other. Is there
a way to go without having to rewrite all these traits specialization?

B.

Specialize the trait. Its hardly much typing! And even less if you
just copy&paste :)

-shez-

Jul 23 '05 #8

P: n/a
That's not that simple. Suppose 'foo' now depends on three parameters,
and I want to specialized foo when all three parameters have a true
std::numeric_limits<>::is_specialized. There is no copy-pasting
possible. And can you just imagine the number of specialized traits I
would have to type?

Jul 23 '05 #9

P: n/a
gao_bo...@voila.fr wrote:
That's not that simple. Suppose 'foo' now depends on three parameters,
and I want to specialized foo when all three parameters have a true
std::numeric_limits<>::is_specialized. There is no copy-pasting
possible. And can you just imagine the number of specialized traits I
would have to type?

All you need is one tag per overloaded 'foo'. And one specialization
per 'TYPE' that 'foo' can operate on.

Jul 23 '05 #10

P: n/a
I am not sure what you mean. For the example I was mentioning, the
enable_if solution would be

// general case
template <typename T1, typename T2, typename T3 >
typename disable_if_c<
numeric_traits<T1>::is_specialized &&
numeric_traits<T2>::is_specialized &&
numeric_traits<T3>::is_specialized
::type foo(const T1 &x1, const T2 &x2, const T3 &x3)
{
/*...*/
}

// case when all types have numeric traits
template <typename T1, typename T2, typename T3 >
typename enable_if_c<
numeric_traits<T1>::is_specialized &&
numeric_traits<T2>::is_specialized &&
numeric_traits<T3>::is_specialized::type

foo(const T1 &x1, const T2 &x2, const T3 &x3)
{
/*...*/
}

What would be the equivalent with your technique? I can see that I
would need only one tag, for the case when all types have numeric
traits, but how would I avoid having a trait with three template
arguments, and having to specialize this trait for all 3-uples of types
with known numeric traits (and there are a lot of them) ? How could I
reuse numeric traits?

Bolin

Jul 23 '05 #11

P: n/a
ga*******@voila.fr wrote:
I am not sure what you mean. For the example I was mentioning, the
enable_if solution would be

// general case
template <typename T1, typename T2, typename T3 >
typename disable_if_c<
numeric_traits<T1>::is_specialized &&
numeric_traits<T2>::is_specialized &&
numeric_traits<T3>::is_specialized
::type

foo(const T1 &x1, const T2 &x2, const T3 &x3)
{
/*...*/
}

// case when all types have numeric traits
template <typename T1, typename T2, typename T3 >
typename enable_if_c<
numeric_traits<T1>::is_specialized &&
numeric_traits<T2>::is_specialized &&
numeric_traits<T3>::is_specialized
::type

foo(const T1 &x1, const T2 &x2, const T3 &x3)
{
/*...*/
}

What would be the equivalent with your technique? I can see that I
would need only one tag, for the case when all types have numeric
traits, but how would I avoid having a trait with three template
arguments, and having to specialize this trait for all 3-uples of types
with known numeric traits (and there are a lot of them) ? How could I
reuse numeric traits?

Bolin


Create a function like this:
template <typename T1, typename T2, typename T3>
void foo(const T1& x1, IsSpecializedTag,
const T2& x2, IsSpecializedTag,
const T3& x3, IsSpecializedTag)
{
/* ... */
}

template <typename T1, typename T2, typename T3>
void foo(const T1& x1, const T2& x2, const T3& x3)
{
typedef typename Trait<T1>::Tag Tag1;
typedef typename Trait<T2>::Tag Tag2;
typedef typename Trait<T3>::Tag Tag3;

return foo(x1, Tag1(),
x2, Tag2(),
x3, Tag3());
}
Of course, you also have the flexibility to mix and match tags like
this:
template <typename T1, typename T2, typename T3>
void foo(const T1& x1, IsSpecializedTag,
const T2& x2, IsSomethingElseTag,
const T3& x3, IsYetSomethingElseTag)
{
/* ... */
}
and 'foo' will "just work" as long as you have assigned an appropriate
tag for each of the possible types (via specialization).

Hope this helps,
-shez-

Jul 23 '05 #12

This discussion thread is closed

Replies have been disabled for this discussion.