P3411 any_view library

I recently published a reference implementation of std::ranges::any_view loosely based on https://wg21.link/P3411, but with some intentional deviations from design choices made in the document, I’ll try my best to be comprehensive about what’s different:

  • The first template parameter, rather than being a value_type, I made it a cv-qualified element_type (like std::span).
  • I chose the name any_kind instead of any_view_options, though I don’t feel strongly about that, just thought the name in the paper was slightly verbose. any_category might also work, except a “category” seems to me like a mutually exclusive set of enumerations rather than flags that can be masked together.
  • I chose to include constexpr support. It helps as a library developer to be able to easily identify potential sources of UB when using compile-time tests. The claim in the paper that SBO prevents constexpr support doesn’t seem very compelling to me, since I was easily able to add an if not consteval guard around the SBO so that constant expressions would not evaluate it and would always fall back to allocating the type-erased adaptors instead.
  • I included move-only support, but as an opt-out feature rather than an opt-in feature. Given that the default template parameter is any_view_options::input (or any_kind::input in my case), it seemed consistent for the default to be type-erasing as many kinds of ranges as possible, while providing a more constrained interface. Making move-only the default and providing any_kind::copyable as a flag to opt into a less-constrained interface that can type-erase fewer kinds of ranges felt like a more consistent choice overall.
  • I added the ability to opt into const-iterable support with any_kind::constant, though I’m not sure this is necessarily the best name, given the potential confusion that it might imply it’s a constant_range<R> (it’s not, it actually causes it to satisfy the exposition-only constraint simple-view<R>), so I’m open to calling it any_kind::simple instead if there’s consensus on that. The paper seems to imply this doubles the amount of template parameters, but I think that’s making the assumption that a const-iterable range should provide both const and non-const overloads. I think it’s easier to understand as a class template if it can only provide one or the other, never both.
  • I conditionally make any_view a common_range<R> if it’s at least any_kind::random_access | any_kind::sized. This felt appropriate, as it was easy to add to the implementation, and allows it to support back (enabled from view_interface) and std::ranges::rbegin in that case. FWIW, common_view<R>::end returns iterator_t<R> if random_access_range<R> and sized_range<R> are both satisfied, which is what motivated me to include this functionality.
  • I removed the rvalue reference template parameter from the class template. This seemed too far removed from the interface that any_view is supposed to provide to allow it as a configurable parameter. If I want rvalue references, I can either specify an rvalue for the reference template parameter, or pipe the any_view to as_rvalue, so I didn’t quite understand why it needed to have its own template parameter.
  • I made any_kind::input an enumeration of 0 rather than 1, as I did not want to decide how to deal with the ability for a user of any_view to be able to express an unsupported kind. I initially tried going the route of supporting an input_or_output kind, and found that use-cases for this were limited, and there were too few guarantees by existing iterator types that don’t satisfy input_iterator to be able to provide a cohesive type-erased adaptor for them that would behave consistently and predictably.

Given these differences between my reference implementation and the existing paper, is GitHub - patrickroberts/pr: Reference implementations inspired by C++ Standard Library proposals still appropriate to integrate into the Beman Project?

1 Like

Welcome @patrickroberts! I think this would make for a great addition to Beman.

Check out the Beman Library Maturity Model. Generally, for a Beman library to get to the “production ready” it needs to implement a target paper without significant deviation, but for the “under development” phase, deviations are fine.

I suggest making your implementation a development phase library right away. If the authors of P3411 don’t like your suggestions, you can publish your own WG21 paper (we can help with that) with your suggestions. Once the paper is published, your library is free to move into “production ready”.

We have a couple ongoing prototypes that aim to help bootstrap Beman libraries which may be useful to you. Mine is here and Zach Laine and others have one here.

If you want to go ahead, say so and we’ll add you as a contributor to the beman org which will give you permissions to create a new repo.

I would love to get started making this a development phase library in the Beman project. I will do so and reach out to the authors of P3411 as well. Thanks for the links to the examplar repo, I will start from there.

I’ve received a response from one of the authors of the proposal, Hui Xie, who made me aware that an R1 draft has been published after feedback received at Wrocław in 2024 available here: `any_view`.

  • It seems that the current draft does indeed use an element type as the first template parameter now.
  • He’s open to any_kind as a candidate name for the options that can be discussed, but it seems there are now other alternatives proposed for the spelling of additional options to be provided to any_view, two of which I would personally be happy to see in the standard instead of an NTTP flag enum approach.
  • Hui has made the case that constexpr support is not useful for type-erasure, and that differing behavior between runtime and compile-time with “if consteval” as a workaround was not desirable as a libc++ implementor.
  • He wanted to leave the default behavior (move-only or copyable) up to the audience at the next SG9 meeting, and prefers copyable to be the default, though I still feel that move-only should be the default.
  • He said that my suggestion for including const-iterable support could be added as a considered option to the next revision.
  • He also made me aware that past discussions on adding common range support did not go well with the committee and that it was not considered to be a very useful thing. I think he’s convinced me that should be excluded as a goal for any_view, since adapting it with views::common provides the same cost-free enhancement I built-in as a special case, but also provides generalized support via an implementation effectively equivalent to wrapping a variant<iterator, sentinel>.
  • He also explained why being able to customize the RValueRef is important, with an example of a zip_view iterator returning a tuple<int &, int &> and wanting an rvalue to be tuple<int &&, int &&> via ADL iter_move as a customization point for the any_view exposition-only iterator type.
  • He was open to my suggestion of the input enumeration value being adjusted, but reiterated that since there are now alternative approaches under consideration, it would potentially make this specific issue moot anyway.

Given this update, I think I will work on an implementation matching R1 draft with “6.2 Alternative Design 2: Single Template Parameter: RangeTraits” and “6.2.1 Optional add-on to RangeTraits”, and as an extension maybe provide a macro switch that allows users of the library to select “6.3 Alternative Design 3: Barry’s Named Template Argument Approach” instead. The only alternative I feel strongly against is “6.1 Alternative Design 1: Variadic Named Template Parameters” because it makes it possible for users to express equivalent-behaving but incompatible any_view instantiations, unless the synopsis is expanded to include constructor and assignment templates to allow conversion between compatible instantiations. This feels unnecessarily complex, and I’d rather have an alternative that does not provide a way to spell multiple instantiations with the same behavior.
And, while it has already been explained to me why constexpr support was opted out of, I’d still like my implementation within the Beman Project to have constexpr support, if that’s an acceptable deviation from the target paper.

Thanks Robert

My view is that it’s acceptable as I believe that almost everything in the standard library is heading towards constexpr support – there’s at least one member of the committee pushing quite hard for that as the default. In part on the grounds of api consistency – that is, as a user having something not work during constexpr that seemingly should is surprising. I’d argue that’s a valid design principle in 2025.

Heavy WIP but I at least got a little something up: GitHub - bemanproject/any_view: A generalized type-erased view with customizable properties

Still need to finish re-implementing everything from my current library, add more tests and examples, and flesh out documentation, but this is at least a sneak-peek at the different designs from R1 that would be supported by this library.

Planning to try and complete the implementation this weekend if time permits.

1 Like

Hey, I can help with adoption, let me know if you need any.

One question that’s been on my mind is how do you deal with documentation when parts of the user interface depend on build configuration?

There’s two configuration parameters, one with 3 options and another with 2, for a total product of 6 possible user interfaces.

I chose my personal preferences as the default options for the configuration, but I’ve never created a library that had a build-configurable user interface before. Should the documentation have dropdown menus at the top of the webpage to switch between the different configuration options?

So this is these settings in part?

BEMAN_ANY_VIEW_USE_FLAGS
BEMAN_ANY_VIEW_USE_NAMED
BEMAN_ANY_VIEW_USE_COPYABLE
BEMAN_ANY_VIEW_USE_MOVE_ONLY

Is this there so that different design options can be explored? Just trying to understand why we’d want all these options.

How about writing a tutorial on how to use?

Yes it’s so that different design options can be explored. The enum flags, type traits, and named templates are three of the four design alternatives proposed in R1. The design alternative in R1 using a template pack was not one I liked, nor apparently the authors of the paper based on the comment at the end of that section, so I didn’t opt to support that alternative. It would add too much complexity to the implementation because of the need to have more constructor and assignment templates.

The move-only vs. copyable option was something I discussed in my email with one of the authors and was told a section would be added for it in R2 because of my suggestion.

Maybe the approach taken at cppreference (see the green text) can serve as an inspiration.

I like it! So display all the declarations in the same page but have green text like (traits, copyable) or (named, move_only), right?

Yep, something like that would be pretty easy to follow.

1 Like