You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This issue is somewhat related to #564. Many if not most libraries pre-C++20 use SFINAE for specializations. This can happen with or without library support (std::enable_if). These specializations can be enabled in several ways:
// enabled via the return typetemplate<classT>
typename std::enable_if<std::is_trivially_default_constructible<T>::value>::type
construct(T*)
{
std::cout << "default constructing trivially default constructible T\n";
}
// enabled via a parametertemplate<classT>
voiddestroy(
T*,
typename std::enable_if<std::is_trivially_destructible<T>::value>::type* = 0)
{
std::cout << "destroying trivially destructible T\n";
}
// enabled via a non-type template parametertemplate<classT,
typename std::enable_if<!std::is_trivially_destructible<T>{} && (std::is_class<T>{} || std::is_union<T>{}), bool>::type = true>
voiddestroy(T* t)
{
std::cout << "destroying non-trivially destructible T\n";
t->~T();
}
// enabled via a type template parametertemplate<classT,
typename = std::enable_if_t<std::is_array<T>::value>>
voiddestroy(T* t)
{
for (std::size_t i = 0; i < std::extent<T>::value; ++i)
destroy((*t)[i]);
}
These conditions to enable function can become much more complex than that, which makes the documentation generated by MrDocs impossible to read. From Boost.Buffers:
For this reason, in documentation, these conditions are seen as independent function constraints that are documented as part of the function requirements in the function details/notes (examples below).
As with #564, whether a condition that leads to substitution failure is a purposeful semantic description of the function requirements might be a matter of intentionality. Luckily, unlike #564, the use of std::enable_if provides the developer with a way to express that intentionality. In fact, clang already has a feature where it interprets std::enable_if as requirements to provide better error messages.
SFINAE in cppreference
Cppreference documents constraints that are usually implemented with SFINAE with notes in the function details. For instance, we can consider this vector::vector overload:
This overload should only be enabled if InputIt satisfies LegacyInputIterator.
template <class_InputIterator,
class_Alloc,
class = enable_if_t<__has_input_iterator_category<_InputIterator>::value>,
class = enable_if_t<__is_allocator<_Alloc>::value> >
vector(_InputIterator, _InputIterator, _Alloc) -> vector<__iter_value_type<_InputIterator>, _Alloc>;
When we look at the documentation of these constrained functions, the function overloads omit their constraints and the function description usually ends with a comment such as:
The comment always follows the pattern "This overload participates in overload resolution only if [the constraint]" but the constraint is sometimes complemented by its rationale.
SFINAE in Doxygen
SFINAE is usually documented in doxygen with macros to omit the std::enable_if portion of the function definition. For instance, boost::urls::ref is documented with:
In the first case, the return value is replaced with __implementation_defined__.
It would usually be replaced with the real return value but the ultimate return value detail::rule_ref<Rule> is an implementation detail that should also be omitted. In this case, the whole expression can be appropriately replaced with __implementation_defined__.
In the second case, the condition is simply omitted:
The developer is responsible for manually describing the condition again in the function details.
However, the solution above does not apply to MrDocs because it expects valid C++ code. If two function declarations have their conditions removed, this will be interpreted as an error because this is a function redeclaration and the functions can't be documented separately. Or in other cases, the functions might become ambiguous and the code that instantiates the functions will also lead to an error.
Proposed solutions
As with #564, neither doxygen nor mrdocs currently have any feature to make that easier. Although there's a viable alternative people have been using for Doxygen, it's far from ideal when there are many constrained functions in a codebase. So this is also a feature where mrdocs has potential to be much more helpful than doxygen.
MrDocs could implement the following solutions:
When MrDocs builds the corpus, as a post-processing step, it would identify these purposeful constraints that use std::enable_if and this information would be included in the Info object.
This automatic detection could be disabled by a config option. Although automatic detection should work most of the time, disabling automatic detection might be useful when the documentation is about std::enable_if itself.
The documentation template pages for these constrained functions should include a message similar to the one in cppreference. Alternatively, the constraints could be rendered as requires clauses, as if the library was a post-C++20 library.
Some other forms of detection of intentional SFINAE could be enabled over time (boost::enable_if and so on).
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.
The text was updated successfully, but these errors were encountered:
Motivation
This issue is somewhat related to #564. Many if not most libraries pre-C++20 use SFINAE for specializations. This can happen with or without library support (
std::enable_if
). These specializations can be enabled in several ways:These conditions to enable function can become much more complex than that, which makes the documentation generated by MrDocs impossible to read. From Boost.Buffers:
For this reason, in documentation, these conditions are seen as independent function constraints that are documented as part of the function requirements in the function details/notes (examples below).
As with #564, whether a condition that leads to substitution failure is a purposeful semantic description of the function requirements might be a matter of intentionality. Luckily, unlike #564, the use of
std::enable_if
provides the developer with a way to express that intentionality. In fact, clang already has a feature where it interpretsstd::enable_if
as requirements to provide better error messages.SFINAE in cppreference
Cppreference documents constraints that are usually implemented with SFINAE with notes in the function details. For instance, we can consider this
vector::vector
overload:This overload should only be enabled if
InputIt
satisfies LegacyInputIterator.Here's how libstdc++ implements this constraint:
where
std::_RequireInputIter<_InputIterator>>
does the job ofstd::enable_if
.And here's how libc++ implements it:
When we look at the documentation of these constrained functions, the function overloads omit their constraints and the function description usually ends with a comment such as:
The comment always follows the pattern "This overload participates in overload resolution only if [the constraint]" but the constraint is sometimes complemented by its rationale.
SFINAE in Doxygen
SFINAE is usually documented in doxygen with macros to omit the
std::enable_if
portion of the function definition. For instance,boost::urls::ref
is documented with:and
boost::urls::lut_chars
is documented with:In the first case, the return value is replaced with
__implementation_defined__
.It would usually be replaced with the real return value but the ultimate return value
detail::rule_ref<Rule>
is an implementation detail that should also be omitted. In this case, the whole expression can be appropriately replaced with__implementation_defined__
.In the second case, the condition is simply omitted:
The developer is responsible for manually describing the condition again in the function details.
However, the solution above does not apply to MrDocs because it expects valid C++ code. If two function declarations have their conditions removed, this will be interpreted as an error because this is a function redeclaration and the functions can't be documented separately. Or in other cases, the functions might become ambiguous and the code that instantiates the functions will also lead to an error.
Proposed solutions
As with #564, neither doxygen nor mrdocs currently have any feature to make that easier. Although there's a viable alternative people have been using for Doxygen, it's far from ideal when there are many constrained functions in a codebase. So this is also a feature where mrdocs has potential to be much more helpful than doxygen.
MrDocs could implement the following solutions:
std::enable_if
and this information would be included in the Info object.std::enable_if
itself.requires
clauses, as if the library was a post-C++20 library.boost::enable_if
and so on).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.
The text was updated successfully, but these errors were encountered: