In the following code, I want to consider functions (Op
s) that have void
return to instead be considered to return true
. The type Retval
, and the return value of Op
are always matching. I'm not able to discriminate using the type traits shown here, and attempts to create a partial template specialization based on Retval
have failed due the presence of the other template variables, Op
and Args
.
How do I specialize only some variables in a template specialization without getting errors? Is there any other way to alter behaviour based on the return type of Op
?
template <typename Retval, typename Op, typename... Args>
Retval single_op_wrapper(
Retval const failval,
char const *const opname,
Op const op,
Cpfs &cpfs,
Args... args) {
try {
CallContext callctx(cpfs, opname);
Retval retval;
if (std::is_same<bool, Retval>::value) {
(callctx.*op)(args...);
retval = true;
} else {
retval = (callctx.*op)(args...);
}
assert(retval != failval);
callctx.commit(cpfs);
return retval;
} catch (CpfsError const &exc) {
cpfs_errno_set(exc.fserrno);
LOGF(Info, "Failed with %s", cpfs_errno_str(exc.fserrno));
}
return failval;
}
You need an explicit specialization, not partial.
template <typename Retval, typename Op, typename... Args>
Retval single_op_wrapper(
Retval const failval,
char const *const opname,
Op const op,
Cpfs &cpfs,
Args... args) {
try {
CallContext callctx(cpfs, opname);
Retval retval;
if (std::is_same<bool, Retval>::value) {
(callctx.*op)(args...);
retval = true;
} else {
retval = (callctx.*op)(args...);
}
assert(retval != failval);
callctx.commit(cpfs);
return retval;
} catch (CpfsError const &exc) {
cpfs_errno_set(exc.fserrno);
LOGF(Info, "Failed with %s", cpfs_errno_str(exc.fserrno));
}
return failval;
}
template<typename Op, typename... Args> void single_op_wrapper<void, Op, Args>(...) {
...
}
Edit: Forgot you were writing a function, not a class.
Template functions cannot be partially specialized. There are different things that you can do: you can wrap the function into a class template with a single static method and specialize the class template, or you can use SFINAE to select the best choice of function among different template functions:
template <typename O, typename Args...>
void single_op_wrapper( /* all but failval */ ) { // [+]
// implementation for void
}
template <typename R, typename O, typename Args...>
typename boost::enable_if< boost::is_same<R,bool>, bool >::type // bool if condition is met
single_op_wrapper( /* all args */ ) {
// implementation for R being a bool
}
template <typename R, typename O, typename Args...>
typename boost::enable_if< boost::is_same<R,char> >::type // by default bool
single_op_wrapper( /* all args */ ) {
// implementation for void return
}
template <typename R, typename O, typename Args...>
typename boost::disable_if_c< boost::is_same<R,char>::value //[*]
|| boost::is_same<R,bool>::value
, R >::type
single_op_wrapper( /* all args */ ) {
// implementation when R is neither bool nor void
}
On the separate template for void [+]:
In C++ you cannot have a function that takes an argument of type void
. That means that you cannot use the same arguments for the void
case as you are using for the rest of them.
On the metaprogramming side:
There are a couple of tricky bits here... the enable_if
is a metafunction that defines an internal type if the condition is met or nothing otherwise. When the compiler tries to substitute the types in the template, the return type will only be valid (and as such the function be a candidate) if the condition is met. The disable_if
metafunction has the opposite behavior. The straight variant enable_if
/ disable_if
take a metafunction as first argument and optionally a type as second argument. The second version enable_if_c
/ disable_if_c
take a boolean as first argument.
It is important in [*] to note that the functions must be exclusive. That is, if for a given type more than one of the templates are candidates, as none of them is an specialization of the others, the compiler will stop with an ambiguity error. That is the reason for using the disable_if
in the last template.
Note: I have used boost
namespace instead of std
as I have not played ever with metaprogramming in c++0x, but I believe that you can change the boost
namespace with std
in your compiler. Check the docs in advance!