feat: add function object support#1163
Conversation
✨ Highlights
🧾 Changes by Scope
🔝 Top Files
|
|
An automated preview of the documentation is available at https://1163.mrdocs.prtest2.cppalliance.org/index.html If more commits are pushed to the pull request, the docs will rebuild at the same URL. 2026-03-13 17:32:42 UTC |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #1163 +/- ##
===========================================
+ Coverage 77.14% 77.44% +0.30%
===========================================
Files 310 313 +3
Lines 29043 29475 +432
Branches 5795 5880 +85
===========================================
+ Hits 22404 22827 +423
+ Misses 4420 4403 -17
- Partials 2219 2245 +26 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
19d7451 to
e9b70af
Compare
alandefreitas
left a comment
There was a problem hiding this comment.
Isn't "functors" formal enough to be used here? It would be a much shorter command and a more concise way to describe it over and over again without needing a "_".
The fixtures should go to better directories, though. Maybe the directories need better documentation, but you can see they shouldn't go there by comparing with the siblings (and you'll see it's definitely not "core", which is used for really core functionality that's not optional). Symbol types and their features are tested in the symbols directory for each symbol. And since there's an option involved, every new option should be tested in the options directory. Then we have new features for variables that should be tested. Then we have a new field in variables, and that's not reflected there. Then we have a folder for javadoc commands that should be tested, and we need a test there because we introduced a new command.
The test cases in symbols/variable should also explore what happens when there is non-conflicting type information, but the variable is forced to be treated as a function object anyway. There are real-world patterns like this. Another pattern we see very often is the combination of template parameters and arguments: impl function with template, different variables with the same function but different params, and so on.
In the existing test, I'm not sure I understand the mechanism through which it goes to the list of functions in the interface. I'm not sure if cppalliance does that or shows it as a separate kind of entity. Because they do behave differently. All niebloids also have a special banner describing how that function works differently. Another thing to explore is how all of that works when one is visible, and the other is an implementation detail, and so on. Another thing to test and design is what happens when documentation goes on the other symbol.
In the output of the existing test, all functors look wrong. They look nothing like niebloids in cppreference. Many of them look arbitrary and not like valid C++ syntax at all. For instance, it tends to look like:
bool single_overload();
(not very different from an overload set with the main function name replaced from operator() to the variable name and with const removed).
rather than:
constexpr /* implementation-defined */ single_overload = {};
bool
operator()() const;
One more thing missing is the presentation of this feature on the landing page. The snippets on the landing page are meant to showcase features that only Mrdocs can support, and this is definitely one of them.
Good work here overall, though. This is a big design decision.
fa5731c to
9ed3493
Compare
alandefreitas
left a comment
There was a problem hiding this comment.
Really nice tests. Everything about this PR is really cool 😎😄
554a41f to
28681e1
Compare
c72c2b2 to
d94c442
Compare
When MrDocs encounters a variable that is a "function object" (its type
is a record whose only public non-special members are operator()
overloads), it now synthesizes free-function entries named after the
variable. The type is marked implementation-defined and hidden from the
output. Multi-overload function objects are naturally grouped by
OverloadsFinalizer.
For example:
struct abs_fn {
double operator()(double x) const noexcept;
};
constexpr abs_fn abs = {};
is documented as if abs were a free function:
double abs(double x) noexcept.
Detection:
Auto-detection: records with only operator() overloads and special
member functions (constructors, destructor, assignment operators).
Controlled by the auto-function-objects config option (default: true).
Explicit: @functionobject or @functor doc commands on the variable or on
the type, e.g. for types that have extra public members. Template types:
auto-detected only when the type lives in the same scope or an inner
scope (e.g. detail::) relative to the variable. This avoids false
positives from unrelated types like std::hash<T>.
What's generated:
Each operator() overload becomes a synthetic FunctionSymbol with the
variable's name, parented under the variable's parent scope. For
variable templates, the synthetic function inherits the variable's
template parameters. A "NOTE: This function object does not participate
in ADL..." disclaimer is appended to each synthetic function's doc.
FunctionSymbol::FunctionObjectImpl back-references the implementation
type.
An example of this feature is added to the landing page (the abs example
above).
Closes cppalliance#564.
This moves isSpecialMemberFunction() out of FunctionObjectFinalizer's unnamed namespace into Function.hpp/cpp as a shared API, and introduces individual helpers (isDefaultConstructor(), isCopyConstructor(), isMoveConstructor(), isCopyAssignment(), isMoveAssignment())---isSpecialMemberFunction() becomes a composition of these individual helpers. Contextually, this fixes three bugs in the existing detection logic in DocComment/Function.hpp: - Default constructors with all-default parameters were not recognized (only empty parameter lists were handled). - Default constructors with parameter packs were not recognized. - By-value copy assignment operator=(X) was not recognized.
d94c442 to
a347aa4
Compare
Edit: This description is now obsolete. Please refer to the commit messages for up to date info.
This PR adds detection of constexpr variables whose type is a record providing only operator() overloads, and documents them as callable objects in the Functions tranche. The type of the variable is hidden as implementation-defined, and breadcrumbs render in f() style for readability, but operator() signatures remain fully visible in the variable synopsis and each overload gets its own detail page---preserving the fact that the callable is a function object, not an ordinary function.
Detection triggers:
Auto-detection (on by default, controlled by
auto-function-objects) for records whose only public non-special members are operator() overloads, including class templates when the variable lives in an enclosing scope of the type.The
@function_objectdocumentation command for cases that fail auto-detection (e.g. types with extra members).Key implementation details:
FunctionObjectFinalizer runs before OverloadsFinalizer: hides the type of the variable, restores operator() to Regular extraction, re-parents overloads under the variable, and appends a fixed disclaimer note.
VariableSymbol gains FunctionObjectOverloads and satisfies the SymbolParent concept, so MultiPageVisitor generates detail pages for each operator() overload.
Handlebars templates render linked operator() signatures in the variable synopsis with "» more..." links to the detail pages.
Closes #564.