-
Notifications
You must be signed in to change notification settings - Fork 181
Prefer T(...) over T{...} when they behave differently in construct, makeUnique and makeShared #225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| #pragma once | ||
|
|
||
| #include <memory> | ||
|
|
||
| #include "rapidcheck/gen/Tuple.h" | ||
| #include "rapidcheck/detail/ApplyTuple.h" | ||
|
|
||
|
|
@@ -119,15 +121,51 @@ class BuildMapper<T, rc::detail::IndexSequence<Indexes...>, Lenses...> { | |
| std::tuple<Lenses...> m_lenses; | ||
| }; | ||
|
|
||
| template<typename T, typename... Args> | ||
| typename std::enable_if<std::is_constructible<T, Args...>::value, T>::type | ||
| construct(Args &&... args) { | ||
| return T(std::forward<Args>(args)...); | ||
| } | ||
|
|
||
| template<typename T, typename... Args> | ||
| typename std::enable_if<!std::is_constructible<T, Args...>::value, T>::type | ||
| construct(Args &&... args) { | ||
| return T{std::forward<Args>(args)...}; | ||
| } | ||
|
|
||
| template<typename T, typename... Args> | ||
| typename std::enable_if<std::is_constructible<T, Args...>::value, std::unique_ptr<T>>::type | ||
| makeUnique(Args &&... args) { | ||
| return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); | ||
| } | ||
|
|
||
| template<typename T, typename... Args> | ||
| typename std::enable_if<!std::is_constructible<T, Args...>::value, std::unique_ptr<T>>::type | ||
| makeUnique(Args &&... args) { | ||
| return std::unique_ptr<T>(new T{std::forward<Args>(args)...}); | ||
| } | ||
|
|
||
| template<typename T, typename... Args> | ||
| typename std::enable_if<std::is_constructible<T, Args...>::value, std::shared_ptr<T>>::type | ||
| makeShared(Args &&... args) { | ||
| return std::make_shared<T>(std::forward<Args>(args)...); | ||
| } | ||
|
|
||
| template<typename T, typename... Args> | ||
| typename std::enable_if<!std::is_constructible<T, Args...>::value, std::shared_ptr<T>>::type | ||
| makeShared(Args &&... args) { | ||
| return std::shared_ptr<T>(new T{std::forward<Args>(args)...}); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This cannot work the same way as above (using This would do one allocation but it would have to move the aggregate in the memory (which would likely be a copy since aggregates are mostly simple data structs). I believe it would still be more efficient than what we have now (but not as perfect as the other case above).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It will require checks for
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I think it's worth it. This solution is not the absolutely most performant but it's the most compatible in combination with relative simplicity.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that it might be better to not strive for absolute generality for the sake of simplicity. And yeah, it will probably be completely insignificant in the larger picture. |
||
| } | ||
| } // namespace detail | ||
|
|
||
| template <typename T, typename... Args> | ||
| Gen<T> construct(Gen<Args>... gens) { | ||
| return gen::map(gen::tuple(std::move(gens)...), | ||
| [](std::tuple<Args...> &&argsTuple) { | ||
| return rc::detail::applyTuple( | ||
| std::move(argsTuple), | ||
| [](Args &&... args) { return T{std::move(args)...}; }); | ||
| std::forward<std::tuple<Args...>>(argsTuple), | ||
| detail::construct<T, Args...> | ||
| ); | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -141,22 +179,20 @@ Gen<std::unique_ptr<T>> makeUnique(Gen<Args>... gens) { | |
| return gen::map(gen::tuple(std::move(gens)...), | ||
| [](std::tuple<Args...> &&argsTuple) { | ||
| return rc::detail::applyTuple( | ||
| std::move(argsTuple), | ||
| [](Args &&... args) { | ||
| return std::unique_ptr<T>(new T{std::move(args)...}); | ||
| }); | ||
| std::forward<std::tuple<Args...>>(argsTuple), | ||
| detail::makeUnique<T, Args...> | ||
| ); | ||
| }); | ||
| } | ||
|
|
||
| template <typename T, typename... Args> | ||
| Gen<std::shared_ptr<T>> makeShared(Gen<Args>... gens) { | ||
| return gen::map(gen::tuple(std::move(gens)...), | ||
| [](std::tuple<Args...> &&argsTuple) { | ||
| return rc::detail::applyTuple(std::move(argsTuple), | ||
| [](Args &&... args) { | ||
| return std::make_shared<T>( | ||
| std::move(args)...); | ||
| }); | ||
| return rc::detail::applyTuple( | ||
| std::forward<std::tuple<Args...>>(argsTuple), | ||
| detail::makeShared<T, Args...> | ||
| ); | ||
| }); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| #pragma once | ||
|
|
||
| #include "rapidcheck.h" | ||
|
|
||
| namespace rc { | ||
| struct Aggregate { | ||
stolyaroleh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| int x; | ||
| int y; | ||
| }; | ||
|
|
||
| template<> | ||
| struct Arbitrary<Aggregate> { | ||
| static Gen<Aggregate> arbitrary() { | ||
| return gen::construct<Aggregate>(gen::arbitrary<int>(), | ||
| gen::arbitrary<int>()); | ||
| } | ||
| }; | ||
|
|
||
| struct NestedAggregate { | ||
| Aggregate a; | ||
| int z; | ||
| }; | ||
| } // namespace rc | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clever fix of the compilation issue. I guess the non-SFINAE friendliness of
result_of, coupled with the fact that it is not defined for function types (but fine for function references; source: https://en.cppreference.com/w/cpp/types/result_of#Notes ) cause the compilation error.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid I'm not following the reasoning for making this an rvalue here. It's not called as an rvalue below so I don't see how this is correct?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::result_of<F(Args...)>::typedoes not work if F is a function (but works if F is a reference to function).Here's an example:
^ fails with
...however, this works:
When I do:
...it fails when it tries to do
result_of<decltype(detail::construct<T, Args...>)(Args...), while I want it to doresult_of<decltype(&detail::construct<T, Args...>)(Args...).https://en.cppreference.com/w/cpp/types/result_of#Notes suggests using
result_of<F&&(Args...)>insteadresult_of<F(Args...)>to avoid this.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course, one might also wonder whether putting
&in front ofdetail::construct<T, Args...>will work. Unfortunately, I never managed to check it since it crashes Clang 3.5.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because of the limitations of
result_ofI think it is better to keep the&&. Technically speaking this is not a r-value reference, but does reference collapsing based on the value category ofCallableso there should be no substantial changes.