One of the (so very many) unfortunate design flaws of C++ is that it is basically impossible to separate implementation from interface when using template metaprogramming.
All over my library I have things like:
template <typename Ma, typename Mb>
typename boost::enable_if_c<
detail::IsMatrix<Ma>::val and detail::IsMatrix<Mb>::val and
detail::MatrixDimensionCheck<Ma,Mb>::isStaticMatch,
bool>::type
operator==(const Ma &a, const Mb &b) {
return detail::matrixEqual(a,b);
}
If this is unreadable, I don't blame you. Most of this mess is simply defining the return type to be bool
if the arguments are matrices and match dimension, and be undefined if they are something else (thus relying on SFINAE to prevent this operator from hiding other important things).
Since the guts of what is essentially a static type-checking function are now embedded into the signature of my ordinary C++ function, these implementation guts will appear in the generated documentation.
I don't want the user to have to read this. All they need to know is that this function returns a bool
(which is almost impossible to tell by reading the above). In the docs, I can explain succinctly, in plain English, that this operator accepts only matrices.
Is there a way to persuade Doxygen to render that type mess as bool
? (I'm assuming there is more or less no way to clean this up in the code directly, but ideas are welcome if you can think of something).
What about:
#ifdef DOXYGEN
#define RETURN_TYPE(Test, Type1) Type1
#else
#define RETURN_TYPE(Test, Type1) typename boost::enable_if_c< Test, Type1 >::type
#endif
template <typename Ma, typename Mb>
RETURN_TYPE((detail::IsMatrix<Ma>::val
and detail::IsMatrix<Mb>::val
and detail::MatrixDimensionCheck<Ma,Mb>::isStaticMatch), bool)
operator==(const Ma &a, const Mb &b) { return detail::matrixEqual(a,b); }
IMHO, it's even easier to read & understand than the initial C++ code. Please notice the double parenthesis in the first macro argument to avoid the compiler to break on the comma in the "Test". You can get rid of it if you reorder the return type first (Type1), and use variable arg macro for the test.
Well, the only way I may achieve this is by duplicating the function definition rather than using the automatic feature of doxygen, and using the @fn
command instead. For your example, something like
/*!@fn template <typename Ma, typename Mb> bool operator==(const Ma &a, const Mb &b)
* @brief My equality operator
* @note The operator is available if the types @c Ma and @c Mb match.
* It will be discarded otherwise
*/
template <typename Ma, typename Mb>
typename boost::enable_if_c<
detail::IsMatrix<Ma>::val and detail::IsMatrix<Mb>::val and
detail::MatrixDimensionCheck<Ma,Mb>::isStaticMatch,
bool>::type
operator==(const Ma &a, const Mb &b) {
return detail::matrixEqual(a,b);
}
should do.
Re persuading Doxygen to show bool
as return type: The only way I know of is Raffi's answer, adding that you then probably want to hide the actual function from Doxygen (several ways to do this).
Re cleaning up: This could look something like
template <typename Ma, typename Mb>
typename bool_isEqual<Ma, Mb>::type
operator==(const Ma &a, const Mb &b)
...
Where bool_isEqual
encapsulates all the template type logic and typedef
s type
to bool
when adequate. (The name bool_isEqual
is chosen because there are assumed to be other template functions with a similar structure that return bool
but have other conditions.)
If this is done consistently, it is probably readable enough.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With