Summary:
This constructor is unsafe. The check it uses before doing the comparison isn't a safe enough check to see if the cast is valid. For example, this is broken in the presence of multiple inheritance because it doesn't adjust the pointer offset to the correct vtable.
e.g.
struct A {
virtual ~A() {};
virtual void doSomething() = 0;
};
struct B {
virtual ~B() {}
virtual void doSomethingElse() = 0;
};
struct C : public B, public A {
virtual ~C() {}
void doSomething() override {
std::cout << "Something!" << std::endl;
}
void doSomethingElse() override {
std::cout << "Something Else!" << std::endl;
}
};
int main (int argc, char **argv) {
auto c = folly::makeFuture<std::shared_ptr<C>>(std::make_shared<C>());
folly::Future<std::shared_ptr<A>> a = std::move(c);
a.get()->doSomething();
return 0;
}
This code will print "Something else!" when run.
Reviewed By: siyengar
Differential Revision:
D3679673
fbshipit-source-id:
dcbf40ca82d458f17ee11191591f8b8daf58c919
return *this;
}
-template <class T>
-template <typename U, typename>
-Future<T>::Future(Future<U>&& other) noexcept
- : core_(detail::Core<T>::convert(other.core_)) {
- other.core_ = nullptr;
-}
-
-template <class T>
-template <typename U, typename>
-Future<T>& Future<T>::operator=(Future<U>&& other) noexcept {
- std::swap(core_, detail::Core<T>::convert(other.core_));
- return *this;
-}
-
template <class T>
template <class T2, typename>
Future<T>::Future(T2&& val)
Future(Future&&) noexcept;
Future& operator=(Future&&) noexcept;
- // conversion constructors
- template <
- class U,
- typename = typename std::enable_if<std::is_convertible<U, T>::value &&
- sizeof(U) == sizeof(T)>::type>
- /* implicit */ Future(Future<U>&&) noexcept;
- template <
- class U,
- typename = typename std::enable_if<std::is_convertible<U, T>::value &&
- sizeof(U) == sizeof(T)>::type>
- /* implicit */ Future& operator=(Future<U>&&) noexcept;
-
/// Construct a Future from a value (perfect forwarding)
template <class T2 = T, typename =
typename std::enable_if<
Core(Core&&) noexcept = delete;
Core& operator=(Core&&) = delete;
- // Core is assumed to be convertible only if the type is convertible
- // and the size is the same. This is a compromise for the complexity
- // of having to make Core truly have a conversion constructor which
- // would cause various other problems.
- // If we made Core move constructible then we would need to update the
- // Promise and Future with the location of the new Core. This is complex
- // and may be inefficient.
- // Core should only be modified so that for size(T) == size(U),
- // sizeof(Core<T>) == size(Core<U>).
- // This assumption is used as a proxy to make sure that
- // the members of Core<T> and Core<U> line up so that we can use a
- // reinterpret cast.
- template <
- class U,
- typename = typename std::enable_if<std::is_convertible<U, T>::value &&
- sizeof(U) == sizeof(T)>::type>
- static Core<T>* convert(Core<U>* from) {
- return reinterpret_cast<Core<T>*>(from);
- }
-
/// May call from any thread
bool hasResult() const {
switch (fsm_.getState()) {
}
}
- // Core should only be modified so that for size(T) == size(U),
- // sizeof(Core<T>) == size(Core<U>).
- // See Core::convert for details.
folly::Function<void(Try<T>&&)> callback_;
// place result_ next to increase the likelihood that the value will be
+++ /dev/null
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/futures/Future.h>
-#include <folly/portability/GTest.h>
-
-#include <type_traits>
-
-using namespace folly;
-
-class A {
- public:
- virtual ~A() {}
-};
-
-class B : public A {
- public:
- explicit B(std::string msg) : msg_(msg) {}
-
- const std::string& getMsg() { return msg_; }
- std::string msg_;
-};
-
-TEST(Convert, subclassConstruct) {
- Future<std::unique_ptr<A>> future =
- makeFuture<std::unique_ptr<B>>(folly::make_unique<B>("hello world"));
- std::unique_ptr<A> a = future.get();
- A* aptr = a.get();
- B* b = dynamic_cast<B*>(aptr);
- EXPECT_NE(nullptr, b);
- EXPECT_EQ("hello world", b->getMsg());
-}
-
-TEST(Convert, subclassAssign) {
- Future<std::unique_ptr<B>> f1 =
- makeFuture<std::unique_ptr<B>>(folly::make_unique<B>("hello world"));
- Future<std::unique_ptr<A>> f2 = std::move(f1);
- std::unique_ptr<A> a = f2.get();
- A* aptr = a.get();
- B* b = dynamic_cast<B*>(aptr);
- EXPECT_NE(nullptr, b);
- EXPECT_EQ("hello world", b->getMsg());
-}
-
-TEST(Convert, ConversionTests) {
- static_assert(std::is_convertible<Future<std::unique_ptr<B>>,
- Future<std::unique_ptr<A>>>::value,
- "Unique ptr not convertible");
- static_assert(!std::is_convertible<Future<std::unique_ptr<A>>,
- Future<std::unique_ptr<B>>>::value,
- "Underlying types are not convertible");
- static_assert(!std::is_convertible<Future<B>, Future<A>>::value,
- "Underlying types are not the same size");
- static_assert(sizeof(detail::Core<std::unique_ptr<A>>) ==
- sizeof(detail::Core<std::unique_ptr<B>>),
- "Sizes of types are not the same");
-}
futures_test_SOURCES = \
../futures/test/CollectTest.cpp \
../futures/test/ContextTest.cpp \
- ../futures/test/ConversionTest.cpp \
../futures/test/CoreTest.cpp \
../futures/test/EnsureTest.cpp \
../futures/test/ExecutorTest.cpp \