jbcoe / value_types Goto Github PK
View Code? Open in Web Editor NEWvalue types for composite class design - with allocators
License: MIT License
value types for composite class design - with allocators
License: MIT License
This is just a question, sorry that I do not know where else to ask this.
I was skimming over the polymorphic_value
papers and noticed that in the original paper small object allocation is encouraged, while in the new paper it is expressly called out as not supported. I would just like to better understand the reason for the change? Was this due to something learned from the implementation, or perhaps a reconsideration of the tradeoffs involved?
The new paper does not provide much explanation, other than saying that a small object implementation would need to be a different type (true, but does not explain why that type was not chosen for the implementation).
Objects constructed in the small buffer may need to be correctly aligned.
Any change needs driving with a suitable test (maybe using ubsan).
Guidance on alignment can be taken from https://lesleylai.info/en/std-align/
This is be clear in the draft and implemented in the ref implementation so this is useable with offset pointer. For instance, this is needed to use with boost::interprocess
allocators
Optional dereference: https://en.cppreference.com/w/cpp/utility/optional/operator*
Fixed by #77
Also including a rational for why this is not supported on Polymorphic (i.e. because you don't know the underlying type to move).
@Ukilele would this be of interest to you?
These must be auto to support pass-through of any return type.
The standard makes some functions conditionally noexcept, we should do the same.
Archive the old repositories and redirect people (users) here.
Blocked by #18
To reproduce the error:
polymorphic_test.cc
:#ifndef XYZ_POLYMORPHIC_USES_EXPERIMENTAL_SMALL_BUFFER_OPTIMIZATION
just before
TEST(PolymorphicTest, InteractionWithPMRAllocators)
polymorphic_sbo_test
There are errors in finding a valid constructor - for some reason allocator traits are trying to pass the allocator to the constructed object. We don't see this with other allocators or with the regular (non-SBO) polymorphic implementation.
My minimal implementations don’t make any of the checks that they perhaps should. We’d get better compiler errors with the addition of concept checks. We need to be sure that incomplete types are still supported (like in indirect_value) though as that’s a useful use case (Pimpl).
Ideally we could check that the newly added requirements are doing the right thing in tests.
I think we can port this from indiect_value but we now have no null state to consider which should make things easier.
We need examples of code that we can write with indirect/polymorphic and what that code would look like without them.
In PR #128 we removed the parameter std::in_place_t
from the variadic constructor of indirect
:
template <class... Ts>
- explicit constexpr indirect(std::in_place_t, Ts&&... ts);
+ explicit constexpr indirect(Ts&&... ts);
The following snippet shows a simplified version of indirect
and a (potentially contrived) scenario, where the variadic constructor might be too greedy:
#include <concepts>
template<class T>
struct indirect {
indirect() = default;
template<class ...Ts>
explicit indirect(Ts&&... ts)
requires std::constructible_from<T, Ts...> {}
};
struct Evil {
Evil(indirect<Evil>&) {}
};
int main() {
indirect<Evil> i1;
indirect<Evil> i2(i1);
}
https://compiler-explorer.com/z/KWvzcnjY9
In the line indirect<Evil> i2(i1);
we don't call the copy constructor of indirect
(as I would expect as a user of indirect
), but we call the variadic constructor as it is a better match. Note that it is only a better match as i1
is non-const. For a const indirect<Evil>
the copy-constructor would still be called.
The first question is whether you all agree with me that indirect<Evil> i2(i1);
should indeed call the copy-constructor or if you think that it is intentional design that it calls the variadic constructor. And if you agree, I think a fix would be to constraint the variadic constructor further. Something like:
template <class... Ts>
explicit constexpr indirect(Ts&&... ts)
- requires std::constructible_from<T, Ts&&...>;
+ requires (std::constructible_from<T, Ts...> &&
+ ( sizeof...(Ts) != 1 || !(std::same_as<std::remove_reference_t<Ts>, indirect> || ...) )
+ );
In prose: If the pack Ts
consists of only one type U
, make sure that U
is not indirect&
.
Requires #22 to be completed
We need to provide rational for the way in which value types have been models, particularly around handling of noexcept-ness which does not follow the Lakos rule.
Requires feedback from #33 to be incorporated.
Go to isocpp.org to submit the paper with a new number
Scheduled to be presented in Kona In November https://github.com/orgs/cplusplus/projects/19/views/1?pane=issue&itemId=41839877
We need slides for a presentation preparing. We can base them on https://github.com/jbcoe/polymorphic_value/blob/main/talks/2022_07_05_CppOnSea.md
#include <optional>
#include <cassert>
#include <https://raw.githubusercontent.com/jbcoe/value_types/main/indirect.h>
template<class T, class U>
T exchange(std::optional<T>& opt, U&& value)
{
assert(opt);
T ret{std::move(*opt)};
*opt = std::forward<U>(value); // avoid unnecessary branch
return ret;
}
int main()
{
// This code will work for any T in place of int...
std::optional<int> oi{3};
auto ri = exchange(oi, 2);
// ... except indirect<T> if we have a specialization for optional<indirect>
std::optional<xyz::indirect<int>> oii{3};
auto rii = exchange(oii, xyz::indirect<int>(2));
}
There was discussion in LWEG suggesting this should be based on regular types, however, this is not inline with the intended design rational which just passes through underlying type operation. Add a discussion point expanding on this.
Fixed by #55
We get code coverage drops because test files are not being covered. Possibly compiler cunning inlines things, possibly something else. At any rate it's annoying noise and should be silenced.
Quis custodiet ipsos custodes?
In the implementation of indirect
and polymporphic
swap
gets called 8 times. Sometimes the member functions indirect::swap
and polymorphic::swap
(e.g. swap(tmp);
) get called. And sometimes std::swap
(https://eel.is/c++draft/utility.swap#lib:swap) on two pointers (e.g. swap(p_, other.p_)
with using std::swap
in the line above) gets called. The point is that swap
gets called unqualified, i.e. it is subject to ADL. But we already know which overloads of swap
we want to call and don't need/want different overloads to be picked by ADL. While there is no advantage in calling swap
unqualified, there might be a disadvantage in doing so: I came up with an example, even though it is very contrived:
template<class T>
struct Holder { T value; };
class Incomplete;
template<class T>
struct X {};
using Indirect = xyz::indirect<X<Holder<Incomplete>>>;
void TestSwap(Indirect& a, Indirect& b)
{
a.swap(b);
}
The snippet does not compile, as a.swap(b)
makes an unqualified call to swap
here. The arguments (p_
and other.p_
) are of type X<Holder<Incomplete>>*
. To perform ADL, the compiler has to instantiate several types, including Holder<Incomplete>
. If you want to protect against this issue, I suggest to fully qualify all calls to swap
.
If you want to have a more in depth discussion of the topic, I can recommend ADL can interfere even with uglified names and What is the std::swap two-step?.
At the discussion in the LWEG group in Kona, Hawaii 2023 it was suggested that this type should follow the Lako rule. However these value types have been build to model std::unique_ptr
and allocator aware types such as std::vector
Use https://github.com/google/benchmark to compare indirect and polymorphic with traditional component-based designs.
Currently allocating tests fail as there is no need to allocate for small objects. We need different objects to force allocation.
See https://godbolt.org/z/75v65MeWY
Behaviour is unexpected, we'd envisaged generated code to be the same (vtable lookup and a method call) - why do we need more indirection?
Hi, this might be a contrived issue.
DRAFT.md#xy13-formatter-support-indirectfmt mentions the specialization of std::formatter<indirect<T, Alloc>, charT>
when the underlying T
supports specialisation of std::formatter<T, charT>
. The format
-function has the following signature (note that it takes the indirect
by const&
):
template<class FormatContext>
typename FormatContext::iterator format(
indirect<T, Alloc> const& value, FormatContext& ctx) const;
But there are some types T
, where you can std::format
a T&
, but not a const T&
. An example for such a type is std::ranges::filter_view<V, P>
. In the following (contrived) example you can see how I can format a filter_view, but not an indirect<filter_view>:
#include <https://raw.githubusercontent.com/jbcoe/value_types/main/indirect.h>
#include <https://raw.githubusercontent.com/jbcoe/value_types/main/polymorphic.h>
#include <vector>
#include <ranges>
#include <print>
struct AllIn {
bool operator()(int i) const { return true; }
};
int main() {
std::vector v{1, 2, 3};
auto filter = std::views::filter(v, AllIn{});
std::println("filter = {}", filter);
using FilterView = decltype(filter);
xyz::indirect<FilterView> indirect(std::in_place, std::views::all(v), AllIn{});
std::println("*indirect = {}", *indirect);
std::println("indirect = {}", indirect); // error
}
So far I think this is a very contrived example and issue. But I at least wanted to make aware of it. In the future it might be worth it to extend formatting of indirect<T, A>
to also work with non-const-formattable types.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.