C
continuation.nomad
I am reading the book C++ Template Metaprogramming. One of the
exercises is as follows:
Write a ternary metafunction replace_type<c,x,y> that takes an
arbitrary compound type c as its first parameter, and replaces all
occurrences of a type x within c with y:
typedef replace_type< void*, void, int >::type t1; // int*
typedef replace_type<int const*[10], int const, long>::type t2; //
long* [10]
typedef replace_type<char& (*)(char&), char&, long&>::type t3; //
long& (*)(long&)
I worked on this for a while and I have what I think is a pretty good
solution. Sorry it's long, but I guess that's how it is with
metaprogramming. Anyway I think my solution handles the general
case. It only does functions with 2 arguments or less but it's
trivial to extend this by copy/pasting a simple partial specialization
and just adding more arguments.
I was wondering if someone could comment on this. Are there any cases
it doesn't handle? Is there a better solution that handles the
general case?
--------------------------------------------------------------------------------------------------------------------
//Helper template. Strips off all *'s and &'s, and allows access to
whatever comes below all of them.
//
//eventual_type<int****&>::type = int
//eventual_type<int>::type = int
//eventual_type<void(****)(int, int)>::type = void(int,int)
template<typename T>
struct eventual_type { typedef T type; };
template<typename T>
struct eventual_type<T&> { typedef typename eventual_type<T>::type
type; };
template<typename T>
struct eventual_type<T*> { typedef typename eventual_type<T>::type
type; };
//True if T is a function after an arbitrary number of levels of
indirection
template<typename T>
struct is_eventually_function
{
static const bool value =
boost::is_function<eventual_type<T>::type>::value;
};
//Helper template to choose a type based on a boolean. If true,
//the first type is chose, otherwise the second.
template<typename Yes, typename No, bool which>
struct choose_type //Default to yes { typedef Yes type; };
template<typename Yes, typename No>
struct choose_type<Yes, No, false> { typedef No type; };
//Helper template to do replacement without recursing through the
'SearchIn' argument
//in the case that it is a function. Replacement occurs only if
SearchIn is exactly
//equal to SearchFor, the only difference being *s and &s. Also adds
the original *s
//and &s back at the end after replacement is complete
template<typename SearchIn, typename SearchFor, typename Replace>
struct primitive_replace
{
typedef typename choose_type<Replace, SearchIn,
boost::is_same<SearchIn, SearchFor>::value>::type type;
};
template<typename SearchIn, typename SearchFor, typename Replace>
struct primitive_replace<SearchIn*, SearchFor, Replace>
{
typedef typename primitive_replace<SearchIn, SearchFor,
Replace>::type* type;
static const int y = 6;
};
template<typename SearchIn, typename SearchFor, typename Replace>
struct primitive_replace<SearchIn&, SearchFor, Replace>
{
typedef typename primitive_replace<SearchIn, SearchFor,
Replace>::type& type;
static const int z = 7;
};
//Generic replacement template. Does replacement in arbitrary types,
recursing through
//nested function arguments.
template<typename SearchIn, typename SearchFor, typename ReplaceWith>
struct replace_type;
//Given a function, replaces all arguments that are exactly equal to
'SearchFor' with
//'ReplaceWith'. If there is an exact match, the replacement happens
immediately. Otherwise
//if a non-matching argument is a function, recursion happens and an
attempt is made on its
//arguments. Note that it is easy to make this template support
arbitrary numbers of function
//arguments. Simply add additional specializations and no other
changes are necessary.
template<typename R, typename SearchFor, typename ReplaceWith>
struct replace_in_function
{
typedef typename replace_type<R, SearchFor, ReplaceWith>::type r;
typedef r(type);
};
template<typename R, typename T1, typename SearchFor, typename
ReplaceWith>
struct replace_in_function<R (T1), SearchFor, ReplaceWith>
{
typedef typename replace_type<R, SearchFor, ReplaceWith>::type r;
typedef typename replace_type<T1, SearchFor, ReplaceWith>::type t1;
typedef r (type)(t1);
};
template<typename R, typename T1, typename T2, typename SearchFor,
typename ReplaceWith>
struct replace_in_function<R (T1, T2), SearchFor, ReplaceWith>
{
typedef typename replace_type<R, SearchFor, ReplaceWith>::type r;
typedef typename replace_type<T1, SearchFor, ReplaceWith>::type t1;
typedef typename replace_type<T2, SearchFor, ReplaceWith>::type t2;
typedef r (type)(t1, t2);
};
//Helper class to split up the logic between functions and non-
functions, so that functions
//are treated differently. The boolean at the end specifies whether
the 'SearchIn' argument
//is a function.
template<typename SearchIn, typename SearchFor, typename ReplaceWith,
bool is_search_func>
struct replace_type_impl //is_search_func=true
{
typedef typename eventual_type<SearchIn>::type
eventual_function_type;
typedef typename replace_in_function<eventual_function_type,
SearchFor, ReplaceWith>::type replaced_eventual_type;
typedef typename primitive_replace<SearchIn, eventual_function_type,
replaced_eventual_type>::type type;
};
template<typename SearchIn, typename SearchFor, typename ReplaceWith>
struct replace_type_impl<SearchIn, SearchFor, ReplaceWith, false>
{
typedef typename primitive_replace<SearchIn, SearchFor,
ReplaceWith>::type type;
};
template<typename SearchIn, typename SearchFor, typename ReplaceWith>
struct replace_type
{
static const bool is_func = is_eventually_function<SearchIn>::value;
static const bool is_exactly_equal = boost::is_same<SearchIn,
SearchFor>::value;
typedef typename replace_type_impl<SearchIn, SearchFor, ReplaceWith,
is_func>::type type_if_not_equal;
typedef typename ReplaceWith type_if_equal;
typedef typename choose_type<type_if_equal, type_if_not_equal,
is_exactly_equal>::type type;
};
Example usage:
typedef void(& funcRefType)(int,int,double);
typedef void(* funcPtrPtrType)(int,int,double);
typedef void(nestedFuncType)(funcRefType, funcPtrType);
typedef replace_in_function<funcRefType, int, char>::type
funcRefReplaced; //void(&)(char,char,double)
typedef replace_in_function<funcPtrType, int, char>::type
funcPtrReplaced; //void(*)(char,char,double)
typedef replace_in_function<nestedFuncType, funcRefType,
funcRefReplaced>::type tempReplace; //void( void(&)(char,char,double),
void(*)(int,int,double)
typedef replace_in_function<tempReplace, funcPtrType,
funcPtrReplaced>::type correctAnswer; //void( void(&)
(char,char,double), void(*)(char,char,double)
typedef replace_in_function<nestedFuncType, int, char>::type
answerToCheck; //void( void(&)(char,char,double), void(*)
(char,char,double)
BOOST_STATIC_ASSERT((boost::is_same<correctAnswer,
answerToCheck>::value));
Email:
f(divisortheory,gmail)
f(a,b) -> (e-mail address removed)
exercises is as follows:
Write a ternary metafunction replace_type<c,x,y> that takes an
arbitrary compound type c as its first parameter, and replaces all
occurrences of a type x within c with y:
typedef replace_type< void*, void, int >::type t1; // int*
typedef replace_type<int const*[10], int const, long>::type t2; //
long* [10]
typedef replace_type<char& (*)(char&), char&, long&>::type t3; //
long& (*)(long&)
I worked on this for a while and I have what I think is a pretty good
solution. Sorry it's long, but I guess that's how it is with
metaprogramming. Anyway I think my solution handles the general
case. It only does functions with 2 arguments or less but it's
trivial to extend this by copy/pasting a simple partial specialization
and just adding more arguments.
I was wondering if someone could comment on this. Are there any cases
it doesn't handle? Is there a better solution that handles the
general case?
--------------------------------------------------------------------------------------------------------------------
//Helper template. Strips off all *'s and &'s, and allows access to
whatever comes below all of them.
//
//eventual_type<int****&>::type = int
//eventual_type<int>::type = int
//eventual_type<void(****)(int, int)>::type = void(int,int)
template<typename T>
struct eventual_type { typedef T type; };
template<typename T>
struct eventual_type<T&> { typedef typename eventual_type<T>::type
type; };
template<typename T>
struct eventual_type<T*> { typedef typename eventual_type<T>::type
type; };
//True if T is a function after an arbitrary number of levels of
indirection
template<typename T>
struct is_eventually_function
{
static const bool value =
boost::is_function<eventual_type<T>::type>::value;
};
//Helper template to choose a type based on a boolean. If true,
//the first type is chose, otherwise the second.
template<typename Yes, typename No, bool which>
struct choose_type //Default to yes { typedef Yes type; };
template<typename Yes, typename No>
struct choose_type<Yes, No, false> { typedef No type; };
//Helper template to do replacement without recursing through the
'SearchIn' argument
//in the case that it is a function. Replacement occurs only if
SearchIn is exactly
//equal to SearchFor, the only difference being *s and &s. Also adds
the original *s
//and &s back at the end after replacement is complete
template<typename SearchIn, typename SearchFor, typename Replace>
struct primitive_replace
{
typedef typename choose_type<Replace, SearchIn,
boost::is_same<SearchIn, SearchFor>::value>::type type;
};
template<typename SearchIn, typename SearchFor, typename Replace>
struct primitive_replace<SearchIn*, SearchFor, Replace>
{
typedef typename primitive_replace<SearchIn, SearchFor,
Replace>::type* type;
static const int y = 6;
};
template<typename SearchIn, typename SearchFor, typename Replace>
struct primitive_replace<SearchIn&, SearchFor, Replace>
{
typedef typename primitive_replace<SearchIn, SearchFor,
Replace>::type& type;
static const int z = 7;
};
//Generic replacement template. Does replacement in arbitrary types,
recursing through
//nested function arguments.
template<typename SearchIn, typename SearchFor, typename ReplaceWith>
struct replace_type;
//Given a function, replaces all arguments that are exactly equal to
'SearchFor' with
//'ReplaceWith'. If there is an exact match, the replacement happens
immediately. Otherwise
//if a non-matching argument is a function, recursion happens and an
attempt is made on its
//arguments. Note that it is easy to make this template support
arbitrary numbers of function
//arguments. Simply add additional specializations and no other
changes are necessary.
template<typename R, typename SearchFor, typename ReplaceWith>
struct replace_in_function
{
typedef typename replace_type<R, SearchFor, ReplaceWith>::type r;
typedef r(type);
};
template<typename R, typename T1, typename SearchFor, typename
ReplaceWith>
struct replace_in_function<R (T1), SearchFor, ReplaceWith>
{
typedef typename replace_type<R, SearchFor, ReplaceWith>::type r;
typedef typename replace_type<T1, SearchFor, ReplaceWith>::type t1;
typedef r (type)(t1);
};
template<typename R, typename T1, typename T2, typename SearchFor,
typename ReplaceWith>
struct replace_in_function<R (T1, T2), SearchFor, ReplaceWith>
{
typedef typename replace_type<R, SearchFor, ReplaceWith>::type r;
typedef typename replace_type<T1, SearchFor, ReplaceWith>::type t1;
typedef typename replace_type<T2, SearchFor, ReplaceWith>::type t2;
typedef r (type)(t1, t2);
};
//Helper class to split up the logic between functions and non-
functions, so that functions
//are treated differently. The boolean at the end specifies whether
the 'SearchIn' argument
//is a function.
template<typename SearchIn, typename SearchFor, typename ReplaceWith,
bool is_search_func>
struct replace_type_impl //is_search_func=true
{
typedef typename eventual_type<SearchIn>::type
eventual_function_type;
typedef typename replace_in_function<eventual_function_type,
SearchFor, ReplaceWith>::type replaced_eventual_type;
typedef typename primitive_replace<SearchIn, eventual_function_type,
replaced_eventual_type>::type type;
};
template<typename SearchIn, typename SearchFor, typename ReplaceWith>
struct replace_type_impl<SearchIn, SearchFor, ReplaceWith, false>
{
typedef typename primitive_replace<SearchIn, SearchFor,
ReplaceWith>::type type;
};
template<typename SearchIn, typename SearchFor, typename ReplaceWith>
struct replace_type
{
static const bool is_func = is_eventually_function<SearchIn>::value;
static const bool is_exactly_equal = boost::is_same<SearchIn,
SearchFor>::value;
typedef typename replace_type_impl<SearchIn, SearchFor, ReplaceWith,
is_func>::type type_if_not_equal;
typedef typename ReplaceWith type_if_equal;
typedef typename choose_type<type_if_equal, type_if_not_equal,
is_exactly_equal>::type type;
};
Example usage:
typedef void(& funcRefType)(int,int,double);
typedef void(* funcPtrPtrType)(int,int,double);
typedef void(nestedFuncType)(funcRefType, funcPtrType);
typedef replace_in_function<funcRefType, int, char>::type
funcRefReplaced; //void(&)(char,char,double)
typedef replace_in_function<funcPtrType, int, char>::type
funcPtrReplaced; //void(*)(char,char,double)
typedef replace_in_function<nestedFuncType, funcRefType,
funcRefReplaced>::type tempReplace; //void( void(&)(char,char,double),
void(*)(int,int,double)
typedef replace_in_function<tempReplace, funcPtrType,
funcPtrReplaced>::type correctAnswer; //void( void(&)
(char,char,double), void(*)(char,char,double)
typedef replace_in_function<nestedFuncType, int, char>::type
answerToCheck; //void( void(&)(char,char,double), void(*)
(char,char,double)
BOOST_STATIC_ASSERT((boost::is_same<correctAnswer,
answerToCheck>::value));
Email:
f(divisortheory,gmail)
f(a,b) -> (e-mail address removed)