[boost][multi] Feedback on `restrictions`
Hi All, "Restrictions" (lazy arrays) in Boost.Multi look like a useful and practical component. The below feedback may sound negative, but it should be treated as suggestions for improving the feature. I find the name "restriction" surprising. Is this a term of art? "Lazy array" immediately conveys the idea, so I wonder why you decided to go with "restriction". I already complained that the "Reference" section is not serving its purpose well. Regarding "restrictions", they are simply missing from that section. Having read the tutorial, and wanting to use the feature in my program, I need to know: 1. Which header to include 2. What is the type returned by the "wedge" operator 3. Is the return type something that I should be able to name, use? Or is the contract "just assign the result to your array, and don't ask for the type name"? 4. What are the preconditions of the operation? 5. Are there things that I can do wrong when using the function and resulting types? 7. Does the function that I provide need to have the same number of arguments as the number of dimensions in the `extents` that I provide? 8.Do you guarantee that the index values that will be passed to subsequent invocations of my function will come in specific order? Or do I need to make sure that my function is pure/regular? 9. Does the function that I provide need to be copyable? 10. Header `restriction.hpp` has functions `make_restriction()` defined. Are they intended for users? If so, they need to be listed in "Reference"' also. I question the choice of using an arbitrary operator for creating lazy arrays. It makes the user code difficult to read and understand. I imagine that in my company I assign a junior developer to fix something in the code, and they are exposed to these bitwise-xor operators. This is an unnecessary confusion. A named factory function like `make_lazy` would immediately sell the intent: it combines the extents with an "indexes-to-value" function to produce a lazy array. It looks like there is an implied concept in your library, "non-mutable array access"., which probably includes being the RHS of the assignment to a "mutable array access". Now I feel even stronger that it should be given a name and be defined, even if semi-formally. The tutorial for restrictions mentions the function ".home()". Is this intended to be part of the library's interface? If so, it must appear in the "Reference" section. The Reference section defines what is the intended interface of the library as opposed to declarations that happen to exist in the headers for other reasons: as temporary solutions, implementation details, experiments or artifacts. Regards, &rzej;
On Sat, Jan 24, 2026 at 7:38 AM Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
Hi All,
Thank you, Andrzej, for the continuous feedback. "Restrictions" (lazy arrays) in Boost.Multi look like a useful and
practical component. The below feedback may sound negative, but it should be treated as suggestions for improving the feature.
I find the name "restriction" surprising. Is this a term of art? "Lazy array" immediately conveys the idea, so I wonder why you decided to go with "restriction".
TL;DR: Using the operator^ is inspired by the "restriction" operator in mathematics, which is either a vertical line (sorry, pipe was already taken) or an *up arrow*, hence op^ (wedge). https://en.wikipedia.org/wiki/Restriction_(mathematics) Long answer: Historically, I started this feature from a private interaction with Sean Parent, in which he said "... You might also look at what would be required to do initialization on construction - for example, *being able to provide generators or transform functions from coordinate to value*." That is, provide a way to map from i,j,k to an element. I interpreted that one wanted to do this in a fundamental way, meaning without late assignment. I hacked something into the constructor, but I was not happy with it because it would not work efficiently with (array) assignment. Hence, I invented this feature; it took me a while to get it right. Sean answered late: "*I like the use of `^` for restrictions *- is this something that can also be piped as a range? `[](auto i, auto j){ return i + j; } ^ multi::extensions_t(3, 4) | column | reverse` -" (he later asked for "infinitely" extended restrictions!). Since the start, I've tried to avoid lazy arrays, lazy expressions, and similar constructs, because I believe they ultimately conspire against neat library designs (see Blitz or Eigen). I think the difference here is that they don't affect the core design of allocated arrays. that remain fundamental. Now, to answer your question, I'm going to paste my answer to Sean's request: I considered using constructors from function objects/lambdas, as well as many other options. But I achieved something more interesting, which is to create lazy multidimensional arrays based on functions defined in a grid of valid coordinates. I call these objects "restrictions," and they are generated from a function object and an extension object from the library ``` auto restriction = [](auto i, auto j) { return i + j; } ^ multi::extensions_t(3, 4); ``` *Using the operator^ is inspired by the "restriction" operator in mathematics, which is either a vertical line (sorry, pipe was already taken) or an *up arrow*, hence op^.* https://en.wikipedia.org/wiki/Restriction_(mathematics) This seems like pure syntactic sugar, but it is not. The restriction object behaves like an array container ("range") in the library as much as it can: it has .begin(), .end(), .size(), extensions(), and .elements() (flatten access). In turn, this allows the use of algorithms (even parallel ones) that can be used for construction (through uninitialized_copy_n) or assignment (copy_n). (It behaves like a supercharged multidimensionally transformed iota) This actually achieves what you were asking for, for example, in construction ``` multi::array<double, 2> A( [](auto i, auto j) { return i + j; } ^ multi::extensions_t(3, 4) ); ``` To clarify, this has the same effect as ``` multi::array<double, 2> A( {3, 4} ); auto [is, js] = A.extensions(); for(auto i : is) for(auto j : js) A[i][j] = i + j; ``` But directly on the construction of A through proper algorithms, (and A can be const, and no raw loops) (both versions are compared here: https://godbolt.org/z/K87ETE18K) These restrictions can do much more, including acting efficiently on assignments ``` A = [](auto i, auto j) { return i + j; } ^ multi::extensions_t(3, 4); // calls copy(rhs.elements().begin(), ...end(), lhs.elements().begin()); allocates if necessary // doesn't call allocate if not necessary! ``` or ``` A == [](auto i, auto j) { return i + j; } ^ multi::extensions_t(3, 4); // calls equal(rhs.elements().begin(), ... end(), lhs.elements().begin()) ``` (Note that the library can dispatch to specialized algorithms, so in GPUs, copy or equal algorithms are parallelized. In other words, these are not necessarily nested loops, which would be terrible. Additionally, restrictions can interact directly with STL algorithms; they don't need intermediate explicitly allocated arrays. Here is the code to play in Godbolt:https://godbolt.org/z/7fb3ajohP Feel free to criticize the design or let me know if it's not headed in the right direction. End of quote.
I already complained that the "Reference" section is not serving its purpose well. Regarding "restrictions", they are simply missing from that section. Having read the tutorial, and wanting to use the feature in my program, I need to know:
You are right; I find writing a formal reference for this part of the library extremely difficult, mainly because it is syntactic and conceptual, and there is little value in specifying classes and member functions. I am willing to try, though.
1. Which header to include
#include <boost/multi/restriction.hpp>
2. What is the type returned by the "wedge" operator
The type is `boost::multi::restriction<2, Functor>`; there is no type erasure. I am not sure whether to make this type an implementation detail, since I didn't find good uses for naming this type. Let alone, I cannot give many guarantees if the Functor instance is not a "pure" function, deterministic, and without side effects. Side note: It seems that one can use the entire library by mentioning only one type `multi::array`. If so, I would be proud of it. (across compilation boundaries, I agree that `subarray` could be useful to be mentioned too).
3. Is the return type something that I should be able to name, use? Or is the contract "just assign the result to your array, and don't ask for the type name"?
That seems to be the case; promoting any other use opens a can of worms. 4. What are the preconditions of the operation?
None? If the function is pure noexcept and has a wide domain contract. Anything outside this can be regarded as a precondition.
5. Are there things that I can do wrong when using the function and resulting types?
It really boils down to the Functor body. If you remain in the "pure" domain, nothing can go wrong; restrictions don't allocate or depend on any external conditions. Of course, an assignment can fail in a variety of ways; allocations could fail, for example. But remember that assignment is a function that `array` or `subarray` controls, not `restriction`. 7. Does the function that I provide need to have the same number of
arguments as the number of dimensions in the `extents` that I provide?
That is a good question. I think it has to have at least some argument dimensions. (Otherwise, the .elements() function will generate something useless). It can have other overloads. One thing I have been debating is whether I should use overloads with fewer arguments (specifically, one). In this way, the Functor can express optimizations, although they have to be semantically correct. It could also be challenging for the user to get this right.
8.Do you guarantee that the index values that will be passed to subsequent invocations of my function will come in specific order? Or do I need to make sure that my function is pure/regular?
Let's put it this way: if you make sure that your function is pure/regular, you are golden. Anything outside this can be powerful and dangerous; it falls into the category "know what you are doing". This is nothing exotic: for example, at the moment, nothing prevents you from writing a random matrix generator like this, if you know what you are doing (to begin, not care in which order the indices are evaluated). ``` multi::array<int, 2> arr = [](auto...) { return rand(); }; ``` 9. Does the function that I provide need to be copyable?
Doesn't need to be copyable in general. (I will probably ask for it to be copyable if you want to copy the restriction itself, which also implies that the restriction that you are using has a state.) This also brings up the topic of "constness" of restrictions. They should apply to the functor itself IMO.
10. Header `restriction.hpp` has functions `make_restriction()` defined. Are they intended for users? If so, they need to be listed in "Reference"' also.
I don't remember.
I question the choice of using an arbitrary operator for creating lazy arrays. It makes the user code difficult to read and understand. I imagine that in my company I assign a junior developer to fix something in the code, and they are exposed to these bitwise-xor operators. This is an unnecessary confusion. A named factory function like `make_lazy` would immediately sell the intent: it combines the extents with an "indexes-to-value" function to produce a lazy array.
Thank you for the suggestion. Do you question the availability of this feature? (Take into account that it is completely opt-in; you need to include a header for this.) Or do you question the name? (My concern is that, like with the word "view", "lazy" can mean anything these days. )
It looks like there is an implied concept in your library, "non-mutable array access"., which probably includes being the RHS of the assignment to a "mutable array access". Now I feel even stronger that it should be given a name and be defined, even if semi-formally.
Yes, the main reason for not defining concepts is that 1) there are many concepts from ranges that already can be used, and I didn't want to introduce near (not subsumed) duplicates, 2) I will probably get it wrong in the first attempt, and 3) since the library is C++17, I wasn't able to dogfooding it. (One thing that I find ironic is that most of the concepts I can think of already exist in one way or another, and the main syntax that introduces new concepts is ".elements()". )
The tutorial for restrictions mentions the function ".home()". Is this intended to be part of the library's interface? If so, it must appear in the "Reference" section. The Reference section defines what is the intended interface of the library as opposed to declarations that happen to exist in the headers for other reasons: as temporary solutions, implementation details, experiments or artifacts.
Good catch, .home() is very fundamental to the design. It is what allows us to write CUDA kernels in a sane way. (it is a context where you can only pass by --trivial-- value). Thanks, Alfredo
sob., 24 sty 2026 o 22:07 Alfredo Correa <alfredo.correa@gmail.com> napisał(a):
On Sat, Jan 24, 2026 at 7:38 AM Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
Hi All,
Thank you, Andrzej, for the continuous feedback.
"Restrictions" (lazy arrays) in Boost.Multi look like a useful and
practical component. The below feedback may sound negative, but it should be treated as suggestions for improving the feature.
I find the name "restriction" surprising. Is this a term of art? "Lazy array" immediately conveys the idea, so I wonder why you decided to go with "restriction".
TL;DR: Using the operator^ is inspired by the "restriction" operator in mathematics, which is either a vertical line (sorry, pipe was already taken) or an *up arrow*, hence op^ (wedge). https://en.wikipedia.org/wiki/Restriction_(mathematics)
Long answer:
Historically, I started this feature from a private interaction with Sean Parent, in which he said "... You might also look at what would be required to do initialization on construction - for example, *being able to provide generators or transform functions from coordinate to value*."
That is, provide a way to map from i,j,k to an element. I interpreted that one wanted to do this in a fundamental way, meaning without late assignment.
I hacked something into the constructor, but I was not happy with it because it would not work efficiently with (array) assignment. Hence, I invented this feature; it took me a while to get it right.
Sean answered late: "*I like the use of `^` for restrictions *- is this something that can also be piped as a range? `[](auto i, auto j){ return i + j; } ^ multi::extensions_t(3, 4) | column | reverse` -" (he later asked for "infinitely" extended restrictions!).
Since the start, I've tried to avoid lazy arrays, lazy expressions, and similar constructs, because I believe they ultimately conspire against neat library designs (see Blitz or Eigen). I think the difference here is that they don't affect the core design of allocated arrays. that remain fundamental.
Now, to answer your question, I'm going to paste my answer to Sean's request:
I considered using constructors from function objects/lambdas, as well as many other options. But I achieved something more interesting, which is to create lazy multidimensional arrays based on functions defined in a grid of valid coordinates.
I call these objects "restrictions," and they are generated from a function object and an extension object from the library
``` auto restriction = [](auto i, auto j) { return i + j; } ^ multi::extensions_t(3, 4); ``` *Using the operator^ is inspired by the "restriction" operator in mathematics, which is either a vertical line (sorry, pipe was already taken) or an *up arrow*, hence op^.* https://en.wikipedia.org/wiki/Restriction_(mathematics)
This seems like pure syntactic sugar, but it is not. The restriction object behaves like an array container ("range") in the library as much as it can: it has .begin(), .end(), .size(), extensions(), and .elements() (flatten access). In turn, this allows the use of algorithms (even parallel ones) that can be used for construction (through uninitialized_copy_n) or assignment (copy_n). (It behaves like a supercharged multidimensionally transformed iota)
This actually achieves what you were asking for, for example, in construction
``` multi::array<double, 2> A( [](auto i, auto j) { return i + j; } ^ multi::extensions_t(3, 4) ); ```
To clarify, this has the same effect as ``` multi::array<double, 2> A( {3, 4} ); auto [is, js] = A.extensions(); for(auto i : is) for(auto j : js) A[i][j] = i + j; ``` But directly on the construction of A through proper algorithms, (and A can be const, and no raw loops) (both versions are compared here: https://godbolt.org/z/K87ETE18K)
These restrictions can do much more, including acting efficiently on assignments
``` A = [](auto i, auto j) { return i + j; } ^ multi::extensions_t(3, 4); // calls copy(rhs.elements().begin(), ...end(), lhs.elements().begin()); allocates if necessary // doesn't call allocate if not necessary! ```
or
``` A == [](auto i, auto j) { return i + j; } ^ multi::extensions_t(3, 4); // calls equal(rhs.elements().begin(), ... end(), lhs.elements().begin()) ```
(Note that the library can dispatch to specialized algorithms, so in GPUs, copy or equal algorithms are parallelized. In other words, these are not necessarily nested loops, which would be terrible. Additionally, restrictions can interact directly with STL algorithms; they don't need intermediate explicitly allocated arrays.
Here is the code to play in Godbolt:https://godbolt.org/z/7fb3ajohP
Feel free to criticize the design or let me know if it's not headed in the right direction.
End of quote.
I already complained that the "Reference" section is not serving its purpose well. Regarding "restrictions", they are simply missing from that section. Having read the tutorial, and wanting to use the feature in my program, I need to know:
You are right; I find writing a formal reference for this part of the library extremely difficult, mainly because it is syntactic and conceptual, and there is little value in specifying classes and member functions. I am willing to try, though.
1. Which header to include
#include <boost/multi/restriction.hpp>
2. What is the type returned by the "wedge" operator
The type is `boost::multi::restriction<2, Functor>`; there is no type erasure. I am not sure whether to make this type an implementation detail, since I didn't find good uses for naming this type. Let alone, I cannot give many guarantees if the Functor instance is not a "pure" function, deterministic, and without side effects.
Side note: It seems that one can use the entire library by mentioning only one type `multi::array`. If so, I would be proud of it. (across compilation boundaries, I agree that `subarray` could be useful to be mentioned too).
3. Is the return type something that I should be able to name, use? Or is the contract "just assign the result to your array, and don't ask for the type name"?
That seems to be the case; promoting any other use opens a can of worms.
4. What are the preconditions of the operation?
None? If the function is pure noexcept and has a wide domain contract. Anything outside this can be regarded as a precondition.
5. Are there things that I can do wrong when using the function and resulting types?
It really boils down to the Functor body. If you remain in the "pure" domain, nothing can go wrong; restrictions don't allocate or depend on any external conditions.
Of course, an assignment can fail in a variety of ways; allocations could fail, for example. But remember that assignment is a function that `array` or `subarray` controls, not `restriction`.
7. Does the function that I provide need to have the same number of
arguments as the number of dimensions in the `extents` that I provide?
That is a good question. I think it has to have at least some argument dimensions. (Otherwise, the .elements() function will generate something useless).
It can have other overloads. One thing I have been debating is whether I should use overloads with fewer arguments (specifically, one). In this way, the Functor can express optimizations, although they have to be semantically correct. It could also be challenging for the user to get this right.
8.Do you guarantee that the index values that will be passed to subsequent invocations of my function will come in specific order? Or do I need to make sure that my function is pure/regular?
Let's put it this way: if you make sure that your function is pure/regular, you are golden. Anything outside this can be powerful and dangerous; it falls into the category "know what you are doing".
This is nothing exotic: for example, at the moment, nothing prevents you from writing a random matrix generator like this, if you know what you are doing (to begin, not care in which order the indices are evaluated).
``` multi::array<int, 2> arr = [](auto...) { return rand(); }; ```
9. Does the function that I provide need to be copyable?
Doesn't need to be copyable in general. (I will probably ask for it to be copyable if you want to copy the restriction itself, which also implies that the restriction that you are using has a state.)
This also brings up the topic of "constness" of restrictions. They should apply to the functor itself IMO.
10. Header `restriction.hpp` has functions `make_restriction()` defined. Are they intended for users? If so, they need to be listed in "Reference"' also.
I don't remember.
I question the choice of using an arbitrary operator for creating lazy arrays. It makes the user code difficult to read and understand. I imagine that in my company I assign a junior developer to fix something in the code, and they are exposed to these bitwise-xor operators. This is an unnecessary confusion. A named factory function like `make_lazy` would immediately sell the intent: it combines the extents with an "indexes-to-value" function to produce a lazy array.
Thank you for the suggestion.
Do you question the availability of this feature? (Take into account that it is completely opt-in; you need to include a header for this.) Or do you question the name? (My concern is that, like with the word "view", "lazy" can mean anything these days. )
It looks like there is an implied concept in your library, "non-mutable array access"., which probably includes being the RHS of the assignment to a "mutable array access". Now I feel even stronger that it should be given a name and be defined, even if semi-formally.
Yes, the main reason for not defining concepts is that 1) there are many concepts from ranges that already can be used, and I didn't want to introduce near (not subsumed) duplicates, 2) I will probably get it wrong in the first attempt, and 3) since the library is C++17, I wasn't able to dogfooding it.
(One thing that I find ironic is that most of the concepts I can think of already exist in one way or another, and the main syntax that introduces new concepts is ".elements()". )
The tutorial for restrictions mentions the function ".home()". Is this intended to be part of the library's interface? If so, it must appear in the "Reference" section. The Reference section defines what is the intended interface of the library as opposed to declarations that happen to exist in the headers for other reasons: as temporary solutions, implementation details, experiments or artifacts.
Good catch, .home() is very fundamental to the design. It is what allows us to write CUDA kernels in a sane way. (it is a context where you can only pass by --trivial-- value).
Thanks, Alfredo
Alfredo, Thank you for the exhaustive reply. I wanted to address two things right away. First, let me clarify: I find the capability to initialize arrays (or assign to them) in this way very useful, and in fact essential for this library. My objection is only to using an overloaded operator for the purpose. Consider an alternative choice, where the user has to write: ``` multi::array<int, 2> const A = restriction([](auto i, auto j) { return 10*i + j; }. multi::extensions_t<2>(3, 2)); ``` I find it superior: if a new program maintainer sees it for the first time, it is easier to grasp what is going on. It is easier to grep for a name. The name already sells the intent. It doesn't have to be spelled "lazy". My point is: it should be a function. Second, about this function's contract. It is conceivable that someone will eventually want to use it like this. I need to populate my const array from a file that I have previously written to. It may occur to me to use a restriction for this purpose: ``` multi::array<int, 2> const A = restriction( [f = std::ofstream("my_array.dat")](auto...) { int i; f >> i; return i; }. multi::extensions_t<2>(3, 2) ); ``` Based on what you explained above it is not an intended use, and it is asking for trouble. But it happens to work, and this may convince me that everything is fine. Because you call it a "lazy constant array", you want operator r[1, 2] to return the same value every time. In order for that to work, the function provided needs to be regular/pure. The function being pure is a *precondition* to your "restrict" operation. It doesn't mean you can check it with an if-statements. It doesn't mean it is a crash or bad memory access when this happens. But it means that you cannot guarantee the behavior of a constant array when the function is not pure. The function being pure *is a precondition*. And the documentation has to say it. Regards, &rzej;
On Sat, Jan 24, 2026 at 3:23 PM Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
Thank you for the exhaustive reply. I wanted to address two things right away. First, let me clarify: I find the capability to initialize arrays (or assign to them) in this way very useful, and in fact essential for this library.
Thank you, I also think that it is fundamental to the library. In fact, I sometimes believe that I unearthed a very powerful and overdue array language in C++. And that is why I want to make it work. Thank you for helping me in this endeavour.
My objection is only to using an overloaded operator for the purpose. Consider an alternative choice, where the user has to write:
``` multi::array<int, 2> const A = restriction([](auto i, auto j) { return 10*i + j; }. multi::extensions_t<2>(3, 2)); ```
I find it superior: if a new program maintainer sees it for the first time, it is easier to grasp what is going on. It is easier to grep for a name. The name already sells the intent. It doesn't have to be spelled "lazy". My point is: it should be a function.
If you are a CTAD believer, the CTAD rules of the `multi::restriction<D, Fun>` type have you covered. ``` multi::array<int, 2> const A = [](auto i, auto j) { return 10*i + j; }) ^ multi::extensions_t<2>(3, 2); multi::array<int, 2> const A = multi::restriction(multi::extensions_t<2>(3, 2), [](auto i, auto j) { return 10*i + j; }); ``` I think the logic of the order is to be consistent with the construction of other (non-lazy) arrays (sizes and then a source of data, a buffer or an allocator in the other cases) Your objection seems against providing (or encouraging?) operator^. In any case, I will add all this to the reference section. Second, about this function's contract. It is conceivable that someone will
eventually want to use it like this. I need to populate my const array from a file that I have previously written to. It may occur to me to use a restriction for this purpose:
``` multi::array<int, 2> const A = restriction( [f = std::ofstream("my_array.dat")](auto...) { int i; f >> i; return i; }. multi::extensions_t<2>(3, 2) ); ``` Based on what you explained above it is not an intended use, and it is asking for trouble. But it happens to work, and this may convince me that everything is fine. Because you call it a "lazy constant array", you want operator r[1, 2] to return the same value every time. In order for that to work, the function provided needs to be regular/pure. The function being pure is a *precondition* to your "restrict" operation.
Yes, you are going in the right direction, although one has to be pragmatic too, requiring functions to be absolutely pure limits them too much. They have to be pure in the context of their lifetime. I would say 'deterministic' is a better name. For example, I don't have objections to this (just an example of a crude way to transpose a matrix) ``` multi::array<int, 2> B({2, 3}); B = ... multi::array<int, 2> const A = multi::restriction( multi::extensions_t<2>(3, 2), [&B](auto i, auto j) { return B[j][i]; } ); ``` Although the function is not technically pure (no?). The biggest problem with your example is not that it is read from an external source or subject to the external environment, but that the fundamental reason is that the result depends on the order of evaluation. The library and the underlying algorithm are free to choose the order in which to evaluate the lazy array elements, or even evaluate them in parallel! It doesn't mean you can check it with an if-statements.
I understand that. I think the precondition is that the function is "deterministic (in its scope)". If you have a better name or if I should choose something better, please let me know. It doesn't mean it is a crash or bad memory access when this happens.
I understand this also. But it means that you cannot guarantee the behavior of a constant array
when the function is not pure. The function being pure *is a precondition*. And the documentation has to say it.
Yes, I will document this. The only mention of this is here: "Element initialization is delegated to the library here. Initialization can use optimal hardware- or library-specific operations (e.g., on a GPU) and *may occur in any order*." I think 'pure' is too restrictive, unless it just means deterministic in the context it is used. (Correct me if I am wrong.) Thanks, Alfredo
On Sat, Jan 24, 2026 at 7:38 AM Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
10. Header `restriction.hpp` has functions `make_restriction()` defined. Are they intended for users? If so, they need to be listed in "Reference"' also.
I remember now by looking at the code. This `make_restriction` is in a detail namespace; it converts a rectangular initializer list into a lazy function for internal use, which is the only clean way to initialize from an initializer list without *intermediate* allocations. Sorry for the confusion, the function is in the detail namespace and therefore still an implementation detail, not for users. The `multi::restriction` constructor (and CTAD) is enough to generate restrictions. There might be `multi::make_restriction` in the future, but mainly to cover cases where CTAD doesn't do the right thing, and one wants to "copy-by-reference-wrapper" with the helper `std::ref`. In the same way that CTAD `std::pair` doesn't 100% replace `std::make_pair`. Thanks, Alfredo
niedz., 25 sty 2026 o 10:07 Alfredo Correa <alfredo.correa@gmail.com> napisał(a):
On Sat, Jan 24, 2026 at 7:38 AM Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
10. Header `restriction.hpp` has functions `make_restriction()` defined. Are they intended for users? If so, they need to be listed in "Reference"' also.
I remember now by looking at the code. This `make_restriction` is in a detail namespace; it converts a rectangular initializer list into a lazy function for internal use, which is the only clean way to initialize from an initializer list without *intermediate* allocations.
Sorry for the confusion, the function is in the detail namespace and therefore still an implementation detail, not for users.
Oh, indeed, it is in namespace detail. Sorry. This is my bad. On the other hand, it is the last declaration in the header. No implementation uses it other than in unit tests. These declarations do not belong in the user-facing header.
The `multi::restriction` constructor (and CTAD) is enough to generate restrictions. There might be `multi::make_restriction` in the future, but mainly to cover cases where CTAD doesn't do the right thing, and one wants to "copy-by-reference-wrapper" with the helper `std::ref`. In the same way that CTAD `std::pair` doesn't 100% replace `std::make_pair`.
I did not express myself clearly. I unnecessarily used a name that is already taken in the library. I do not propose to use or overuse CTADs. I intended to propose a named factory function. I have now filed an issue for this that explains this better. https://github.com/correaa/boost-multi/issues/123 Regards, &rzej;
Thanks, Alfredo
On Sun, Jan 25, 2026 at 6:44 AM Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
niedz., 25 sty 2026 o 10:07 Alfredo Correa <alfredo.correa@gmail.com> napisał(a):
On Sat, Jan 24, 2026 at 7:38 AM Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
10. Header `restriction.hpp` has functions `make_restriction()` defined. Are they intended for users? If so, they need to be listed in "Reference"' also.
I remember now by looking at the code. This `make_restriction` is in a detail namespace; it converts a rectangular initializer list into a lazy function for internal use, which is the only clean way to initialize from an initializer list without *intermediate* allocations.
Sorry for the confusion, the function is in the detail namespace and therefore still an implementation detail, not for users.
Oh, indeed, it is in namespace detail. Sorry. This is my bad. On the other hand, it is the last declaration in the header. No implementation uses it other than in unit tests. These declarations do not belong in the user-facing header.
Yes, I haven’t decided where to put these. I am in the process to actually moving the restrictions implementation to its own header. For the moment, trust the namespace location, not the physical location in the header.
The `multi::restriction` constructor (and CTAD) is enough to generate restrictions. There might be `multi::make_restriction` in the future, but mainly to cover cases where CTAD doesn't do the right thing, and one wants to "copy-by-reference-wrapper" with the helper `std::ref`. In the same way that CTAD `std::pair` doesn't 100% replace `std::make_pair`.
I did not express myself clearly. I unnecessarily used a name that is already taken in the library. I do not propose to use or overuse CTADs. I intended to propose a named factory function. I have now filed an issue for this that explains this better. https://github.com/correaa/boost-multi/issues/123
I like it. Would it be ok if the factory has the arguments reversed with respect to the constructor? I think so. I wouldn’t even mind if the restriction constructor(s) were private. <https://github.com/correaa/boost-multi/issues/123>
Regards, &rzej;
Thanks, Alfredo
participants (2)
-
Alfredo Correa -
Andrzej Krzemienski