Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hiding template implementation details from Doxygen

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).

like image 344
trbabb Avatar asked Aug 08 '13 08:08

trbabb


3 Answers

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.

like image 66
xryl669 Avatar answered Oct 19 '22 11:10

xryl669


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.

like image 36
Raffi Avatar answered Oct 19 '22 09:10

Raffi


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 typedefs 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.

like image 1
Gerhard Avatar answered Oct 19 '22 10:10

Gerhard