cppalliance / mrdocs

MrDocs: A Clang/LLVM tool for building reference documentation from C++ code and javadoc comments.
https://mrdocs.com
Other
75 stars 16 forks source link

identify niebloids #564

Open alandefreitas opened 5 months ago

alandefreitas commented 5 months ago

Motivation

Many libraries, such as Boost.URL and Boost.Buffers include Niebloids. For instance:

https://github.com/cppalliance/buffers/blob/develop/include/boost/buffers/algorithm.hpp#L182

https://github.com/boostorg/url/blob/6c48a8c4e133d3b8d5b7113e6d8cae0ac48293cd/include/boost/url/grammar/digit_chars.hpp#L78 (The pair digit_chars/digit_chars_t is a niebloid if find_if/find_if_not "doesn't count" because they're considered private)

Definition

MrDocs needs a strategy or feature to support documenting Niebloids. From the standard:

The entities defined in the std​::​ranges namespace in this Clause are not found by argument-dependent name lookup (basic.lookup.argdep). When found by unqualified (basic.lookup.unqual) name lookup for the postfix-expression in a function call, they inhibit argument-dependent name lookup.

void foo() { using namespace std::ranges; std::vector vec{1,2,3}; find(begin(vec), end(vec), 2); // #1 } The function call expression at #1 invokes std​::​ranges​::​find, not std​::​find, despite that (a) the iterator type returned from begin(vec) and end(vec) may be associated with namespace std and (b) std​::​find is more specialized ([temp.func.order]) than std​::​ranges​::​find since the former requires its first two parameters to have the same type.

Eric Niebler himself suggested the name and wrote an article in 2014 explaining this concept.

Cppreference describes it as

The function-like entities described on this page are niebloids, that is:

  • Explicit template argument lists may not be specified when calling any of them.
  • None of them is visible to argument-dependent lookup.
  • When one of them is found by normal unqualified lookup for the name to the left of the function-call operator, it inhibits argument-dependent lookup.

Properties of Niebloids

An inline variable pair and a functor are considered a niebloid if they have the following properties:

... a CPO is an object (not a function); it’s callable; it’s constexpr-constructible, [...] it’s customizable (that’s what it means to “interact with program-defined types”); and it’s concept-constrained. [...] If you remove the adjectives “customizable, concept-constrained” from the above, then you have a function object that turns off ADL — but is not necessarily a customization point. The C++2a Ranges algorithms, such as std::ranges::find, are like this. Any callable, constexpr-constructible object is colloquially known as a “niebloid,” in honor of Eric Niebler.

MrDocs can use these properties to attempt to automatically identify Niebloids.

Niebloids in cppreference

Cppreference also documents niebloids as it own thing (not as class/struct or function overloads):

image

When we look at the documentation of these niebloids, the documentation is very similar to function overloads where the function names come from the inline variable and the function arguments and return type come from the functor. Pages documenting niebloids usually end with:

image

It's important to note the struct defining the niebloid and the constexpr inline variable are aggregated and documented as the same function-like entity (the niebloid). There's no individual page for the variable and for the functor.

Niebloids in Doxygen

Libraries such as range-v3 attempt to document niebloids as function overloads but the code is a mess.

https://github.com/ericniebler/range-v3/blob/53c40dd628450c977ee1558285ff43e0613fa7a9/include/range/v3/algorithm/copy.hpp#L40

It depends on lots of macros so that these symbols are identified as classes by the compiler and identified as function overloads by doxygen. The code becomes very hard to read.

Proposed solutions

Neither doxygen nor mrdocs currently have any feature to make that easier. I think niebloids are going to be the first feature where we can confidently say mrdocs does something doxygen can’t do.

MrDocs could implement a combination of the following solutions:

This is a general idea. Considering the complexity of the problem, implementation requirements can only be completely identified when we start working on it. We will certainly find more obstacles.

Quuxplusone commented 5 months ago

That's an excellent writeup IMHO. :) Your point about how std::hash<T> shouldn't be documented as a niebloid (even if some rando creates an template<class T> inline constexpr auto myHash = std::hash<T>() in another file) is important and non-obvious.

Further heuristics might include: