doxygen / doxygen

Official doxygen git repository
https://www.doxygen.org
GNU General Public License v2.0
5.57k stars 1.27k forks source link

Issue with Documenting Mixin Classes #8847

Open psalvaggio opened 2 years ago

psalvaggio commented 2 years ago

Describe the bug

Consider the following construct:

/** The base class for mixins to allow communications between mixins
 *
 *  \tparam Mixin      The mixin for which this class is serving as the base.
 *                     This is here for disambiguation between base classes.
 *  \tparam MixinType  mixin<Mixins...>, the mixin type with all used mixins
 */
template <template <class...> class Mixin, class MixinType>
struct MixinBase {
  /// Type alias for the mixin class
  using self_t = MixinType;

  /** Get a reference to the mixin object, can be used from a mixin to
   *  communicate with other mixins
   */
  /// \{
  decltype(auto) self() & { return static_cast<self_t&>(*this); }
  decltype(auto) self() && { return static_cast<self_t&&>(*this); }
  decltype(auto) self() const& { return static_cast<const self_t&>(*this); }
  decltype(auto) self() const&& { return static_cast<const self_t&&>(*this); }
  /// \}
};

/** Main mixin template, mixin classes should inherit from this template.
 *
 *  When inheriting from this template, the class will inherit from each mixin.
 *  For example:
 *
 *  \code
 *  template <class Base>
 *  struct A : Base {
 *    int a;
 *  };
 *
 *  template <class Base>
 *  struct B : Base {
 *    int b;
 *  };
 *
 *  class C : public mixin<A, B> {};
 *  \endcode
 *
 *  In this case, the inheritance tree would look like:
 *  \dot
 *  digraph inheritance_tree {
 *    node [shape=oval, fontname=Courier, fontsize=10];
 *    C
 *    m [label="mixin<A, B>"]
 *    A [label="A<MixinBase<A, mixin<A, B>>>"]
 *    B [label="B<MixinBase<B, mixin<A, B>>>"]
 *    mbA [label="MixinBase<A, mixin<A, B>>"]
 *    mbB [label="MixinBase<B, mixin<A, B>>"]
 *    C -> m
 *    m -> A
 *    m -> B
 *    A -> mbA
 *    B -> mbB
 *  }
 *  \enddot
 *  And \c C will wind up with the \c a and \c b public data members.
 */
template <template <class...> class... Mixins>
struct mixin : Mixins<MixinBase<Mixins, mixin<Mixins...>>>... {};

In the example in the comment for mixin, class C should have the following "effective" interface:

class C {
  public:
    int a;
    int b;
};

However, it gains a and b through inheritance and these members never show up in C's public interface. When I generate Doxygen for this situation, I wind up getting an empty page. Doxygen considers C's inheritance tree to be:

C -> mixin<A, B> -> A<MixinBase<A, mixin<A, ...>>>

I'm not quite sure what is going wrong here, but I think it has to do with the variadic template expansion:

struct mixin : Mixins<MixinBase<Mixins, mixin<Mixins...>>>... {};

I also tried with a build with Clang-assisted parsing, thinking that might help, but it produced the same result.

Expected behavior For this case, I would expect a section of

Public Attributes Inherited from `A<MixinBase<A, mixin<A, B>>>`
Public Attributes Inherited from `B<MixinBase<B, mixin<A, B>>>`
Public Attributes Inherited from `MixinBase<A, mixin<A, B>>`
Public Attributes Inherited from `MixinBase<B, mixin<A, B>>`

This is a pretty complicated setup, as the inheritance structure for mixin changes with its template parameters, however there are no specializations, only different instantiations across the codebase. Also aware that the use cases for variadic inheritance are few and far between, so I can't really say I was expecting this to work, but if it is possible, it would really help the documentation of such classes.

albert-github commented 2 years ago
psalvaggio commented 2 years ago

mixin-doxy.zip I am using Doxygen 1.9.2. This example actually fared a little better that it does in my codebase, as it was able to get the inherited member from A, but it still shows the issue with the variadic inheritance.

psalvaggio commented 2 years ago

mixin-doxy.zip I made the example a bit more realistic to my codebase, where there is normally one layer of inheritance sitting between my end types and the mixin and shows the issue I am directly seeing: image