Last active
October 6, 2015 22:51
-
-
Save dmh2000/799bf0472527b7a654c1 to your computer and use it in GitHub Desktop.
kind of simple example for how 'traits' work
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdio> | |
#include <cstdint> | |
#include <limits> | |
/** | |
USING TAGGED DISPATCH FOR GENERIC IF-THEN-ELSE | |
Or, sort of a contrived example that illustrates more or less how 'tagged disppatch' in the STL work | |
*/ | |
/** PROBLEM : | |
We are writing some library routines that will be consumed by various clients. | |
We need two functions, one that has an integer input argument | |
and one that has a double input argument. Most of the code for the two | |
versions is identical except for one piece. That part is different based | |
on whether the the top level input argument is integer or double. | |
We decide to write two overloads of the function, one that is overloaded for integers | |
and the other for double. This is a typical approach, but it has a drawback: | |
it requires a lot of duplicated code. | |
yes we could factor out the identical parts as much as possible but having two versions | |
is still kind of klunky. | |
tagged dispatch is supported by the std::traits templates | |
*/ | |
// this represents a configuration parameter that might be different for different builds. | |
const int32_t CONFIGURATION_PARAMETER = 1; | |
// ============================================ | |
// versions selected by name | |
// (can't be overloaded by return value only) | |
// ============================================ | |
// version called by integer clients | |
int32_t fi(int32_t config) { | |
// use the configuration | |
printf("fi(int32_t)\n"); | |
return 0; // return whatever results | |
} | |
// version called by floating point clients | |
double ff(int32_t config) { | |
printf("ff(double)\n"); | |
return 0.0; // return whatever results | |
} | |
// can't use overloading because fi and ff only differ by return type | |
// so have to implement two versions | |
// functions fa(int32_t) and fa(double) are identical except for the | |
// calls to 'fi' and 'ff' depending on the type | |
// INTEGER VERSION | |
int32_t fa(int32_t i) { | |
int32_t k; | |
// ======================= | |
// .... lots of code here | |
// ======================= | |
k = fi(CONFIGURATION_PARAMETER); // call integer version | |
// ======================= | |
// .... lots of code here | |
// ======================= | |
return k; | |
} | |
// FLOATING POINT VERSION | |
double fa(double f) { | |
double k; | |
// ======================= | |
// .... lots of code here | |
// ======================= | |
k = ff(CONFIGURATION_PARAMETER); // call float version | |
// ======================= | |
// .... lots of code here | |
// ======================= | |
return k; | |
} | |
// ==================================================================================== | |
// because 'ff' and 'fi' differ only by return type (they both have an integer as their | |
// only parameter), we can't use an overload to call the right ones. | |
// So here's what we would like to have is a generic | |
// template that executes the proper code based on the parameter type | |
// ==================================================================================== | |
/* | |
template<typename T> T f(T t) { | |
T k; | |
// ======================= | |
// .... lots of code here | |
// ======================= | |
// how do you do this if your input 'T' can't be used to | |
// resolve an overload. | |
if (T is an integer type) { | |
k = fi(CONFIGURATION_PARAMETER); // call the integer version | |
} | |
else if (T is a floating point type) | |
k = ff(CONFIGURATION_PARAMETER) // call the floating point version | |
} | |
// ======================= | |
// .... lots of code here | |
// ======================= | |
return k; | |
} | |
*/ | |
// ================================================================= | |
// GENERIC VERSION | |
// ================================================================= | |
/** | |
It is possible to write a generic template, using the 'tagged dispaatch' convention | |
to create the equivalent of an if-then-else based on types rather than values. | |
Proceed as follows: | |
*/ | |
// step 1 : define a traits tag struct for each alternative | |
// any name is ok | |
struct integer_traits_tag {}; | |
struct double_traits_tag {}; | |
// step 2 : create a generate 'traits' type | |
template<typename T> struct number_traits { | |
// generic version has no innards | |
}; | |
// step 3a : specialize the traits type for integer | |
template<> struct number_traits<int32_t> { | |
// declare a typedef 'type' for this specialization using the appropriate traits tag | |
typedef integer_traits_tag type; | |
}; | |
// step 3b : specialize the traits type for double | |
template<> struct number_traits<double> { | |
// declare a typedef 'type' for this specialization using the appropriate traits tag | |
typedef double_traits_tag type; | |
}; | |
// step 4: create two overloaded functions, one for the integer version and one for the floating point version. | |
// differentiate them by the traits tag, which will be used to force the correct overload | |
int32_t fx(int32_t config, integer_traits_tag) | |
{ | |
// INTEGER VERSION | |
// do something with 'config' and return a value | |
printf("fx<int32_t>\n"); | |
return 1; | |
} | |
double fx(int32_t config, double_traits_tag) | |
{ | |
// FLOATING POINT VERSION | |
// do something with 'config' and return a value | |
printf("fx<double>\n"); | |
return 0.0; | |
} | |
// since the two versions have a lot of duplicated code, | |
// we want a generic version that calls the right function based on type and eliminate the extra code. | |
template<typename T> T fb(T t) { | |
T k; | |
// ======================= | |
// .... lots of code here | |
// ======================= | |
// force the correct overload by the traits type argument | |
k = fx(CONFIGURATION_PARAMETER, number_traits<T>::type()); | |
// ======================= | |
// .... lots of code here | |
// ======================= | |
return k; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
int32_t a; | |
double b; | |
a = fa(1); | |
b = fa(1.0); | |
a = fb(1); | |
b = fb(1.0); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment