/* * Copyright 2016 Google LLC * * 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. */ #ifndef FIREBASE_APP_CLIENT_CPP_SRC_INCLUDE_FIREBASE_FUTURE_H_ #define FIREBASE_APP_CLIENT_CPP_SRC_INCLUDE_FIREBASE_FUTURE_H_ #include #include #include #include "firebase/internal/common.h" #ifdef FIREBASE_USE_STD_FUNCTION #include #endif namespace firebase { // Predeclarations. /// @cond FIREBASE_APP_INTERNAL namespace detail { class FutureApiInterface; } // namespace detail /// @endcond /// Asynchronous call status. enum FutureStatus { /// Results are ready. kFutureStatusComplete, /// Result is still being processed. kFutureStatusPending, /// No result is pending. /// FutureBase::Release() or move operator was called. kFutureStatusInvalid }; /// Handle that the API uses to identify an asynchronous call. /// The exact interpretation of the handle is up to the API. typedef uintptr_t FutureHandle; /// @brief Type-independent return type of asynchronous calls. /// /// @see Future for code samples. /// /// @cond FIREBASE_APP_INTERNAL /// Notes: /// - Futures have pointers back to the API, but the API does not maintain /// pointers to its Futures. Therefore, all Futures must be destroyed /// *before* the API is destroyed. /// - Futures can be moved or copied. Call results are reference counted, /// and are destroyed when they are long longer referenced by any Futures. /// - The actual `Status`, `Error`, and `Result` values are kept inside the /// API. This makes synchronization and data management easier. /// /// WARNING: This class should remain POD (plain old data). It should not have /// virtual methods. Nor should the derived Future class add any /// data. Internally, we static_cast FutureBase to Future, /// so the underlying data should remain the same. /// @endcond class FutureBase { public: /// Function pointer for a completion callback. When we call this, we will /// send the completed future, along with the user data that you specified /// when you set up the callback. typedef void (*CompletionCallback)(const FutureBase& result_data, void* user_data); /// Construct an untyped future. FutureBase(); /// @cond FIREBASE_APP_INTERNAL /// Construct an untyped future using the specified API and handle. /// /// @param api API class used to provide the future implementation. /// @param handle Handle to the future. FutureBase(detail::FutureApiInterface* api, FutureHandle handle); /// @endcond ~FutureBase(); /// Copy constructor and operator. /// Increment the reference count when creating a copy of the future. FutureBase(const FutureBase& rhs); /// Copy an untyped future. FutureBase& operator=(const FutureBase& rhs); #if defined(FIREBASE_USE_MOVE_OPERATORS) /// Move constructor and operator. /// Move is more efficient than copy and delete because we don't touch the /// reference counting in the API. FutureBase(FutureBase&& rhs); /// Copy an untyped future. FutureBase& operator=(FutureBase&& rhs); #endif // defined(FIREBASE_USE_MOVE_OPERATORS) /// Explicitly release the internal resources for a future. /// Future will become invalid. void Release(); /// Completion status of the asynchronous call. FutureStatus status() const; /// When status() is kFutureStatusComplete, returns the API-defined /// error code. Otherwise, return value is undefined. int error() const; /// When status() is kFutureStatusComplete, returns the API-defined error /// message, as human-readable text, or an empty string if the API does not /// provide a human readable description of the error. /// /// @note The returned pointer is only valid for the lifetime of the Future /// or its copies. const char* error_message() const; /// Result of the asynchronous call, or nullptr if the result is still /// pending. Cast is required since GetFutureResult() returns void*. const void* result_void() const; /// Register a callback that will be called at most once, when the future is /// completed. /// /// If you call any OnCompletion() method more than once, only the most recent /// callback you registered will be called. /// /// When your callback is called, the user_data that you supplied here will be /// passed back as the second parameter. /// /// @param[in] callback Function pointer to your callback. /// @param[in] user_data Optional user data. We will pass this back to your /// callback. void OnCompletion(CompletionCallback callback, void* user_data) const; #if defined(FIREBASE_USE_STD_FUNCTION) || defined(DOXYGEN) /// Register a callback that will be called at most once, when the future is /// completed. /// /// If you call any OnCompletion() method more than once, only the most recent /// callback you registered will be called. /// /// @param[in] callback Function or lambda to call. /// /// @note This method is not available when using STLPort on Android, as /// std::function is not supported on STLPort. void OnCompletion(std::function callback) const; #endif // defined(FIREBASE_USE_STD_FUNCTION) || defined(DOXYGEN) /// Returns true if the two Futures reference the same result. bool operator==(const FutureBase& rhs) const { return api_ == rhs.api_ && handle_ == rhs.handle_; } /// Returns true if the two Futures reference different results. bool operator!=(const FutureBase& rhs) const { return !operator==(rhs); } #if defined(INTERNAL_EXPERIMENTAL) /// Returns the API-specific handle. Should only be called by the API. FutureHandle GetHandle() const { return handle_; } #endif // defined(INTERNAL_EXPERIMENTAL) protected: /// @cond FIREBASE_APP_INTERNAL /// Backpointer to the issuing API class. /// Set to nullptr when Future is invalidated. detail::FutureApiInterface* api_; /// API-specified handle type. FutureHandle handle_; /// @endcond }; /// @brief Type-specific version of FutureBase. /// /// The Firebase C++ SDK uses this class to return results from asynchronous /// operations. All Firebase C++ functions and method calls that operate /// asynchronously return a Future, and provide a "LastResult" function to /// retrieve the most recent Future result. /// /// @code /// // You can retrieve the Future from the function call directly, like this: /// Future< SampleResultType > future = firebase::SampleAsyncOperation(); /// /// // Or you can retrieve it later, like this: /// firebase::SampleAsyncOperation(); /// // [...] /// Future< SampleResultType > future = /// firebase::SampleAsyncOperationLastResult(); /// @endcode /// /// When you have a Future from an asynchronous operation, it will eventually /// complete. Once it is complete, you can check for errors (a nonzero error() /// means an error occurred) and get the result data if no error occurred by /// calling result(). /// /// There are two ways to find out that a Future has completed. You can poll /// its status(), or set an OnCompletion() callback: /// /// @code /// // Check whether the status is kFutureStatusComplete. /// if (future.status() == firebase::kFutureStatusComplete) { /// if (future.error() == 0) { /// DoSomethingWithResultData(future.result()); /// } /// else { /// LogMessage("Error %d: %s", future.error(), future.error_message()); /// } /// } /// /// // Or, set an OnCompletion callback, which accepts a C++11 lambda or /// // function pointer. You can pass your own user data to the callback. In /// // most cases, the callback will be running in a different thread, so take /// // care to make sure your code is thread-safe. /// future.OnCompletion([](const Future< SampleResultType >& completed_future, /// void* user_data) { /// // We are probably in a different thread right now. /// if (completed_future.error() == 0) { /// DoSomethingWithResultData(completed_future.result()); /// } /// else { /// LogMessage("Error %d: %s", /// completed_future.error(), /// completed_future.error_message()); /// } /// }, user_data); /// @endcode /// /// @tparam ResultType The type of this Future's result. // // WARNING: This class should not have virtual methods or data members. // See the warning in FutureBase for further details. template class Future : public FutureBase { public: /// Function pointer for a completion callback. When we call this, we will /// send the completed future, along with the user data that you specified /// when you set up the callback. typedef void (*TypedCompletionCallback)(const Future& result_data, void* user_data); /// Construct a future. Future() {} /// @cond FIREBASE_APP_INTERNAL /// Construct a future using the specified API and handle. /// /// @param api API class used to provide the future implementation. /// @param handle Handle to the future. Future(detail::FutureApiInterface* api, FutureHandle handle) : FutureBase(api, handle) {} /// @endcond /// Result of the asynchronous call, or nullptr if the result is still /// pending. Allows the API to provide a type-specific interface. /// const ResultType* result() const { return static_cast(result_void()); } /// Register a callback that will be called at most once, when the future is /// completed. /// /// If you call any OnCompletion() method more than once, only the most recent /// callback you registered will be called. /// /// When your callback is called, the user_data that you supplied here will be /// passed back as the second parameter. /// /// @param[in] callback Function pointer to your callback. /// @param[in] user_data Optional user data. We will pass this back to your /// callback. /// /// @note This is the same callback as FutureBase::OnCompletion(), so you /// can't expect to set both and have both run; again, only the most recently /// registered one will run. void OnCompletion(TypedCompletionCallback callback, void* user_data) const { FutureBase::OnCompletion(reinterpret_cast(callback), user_data); } #if defined(FIREBASE_USE_STD_FUNCTION) || defined(DOXYGEN) /// Register a callback that will be called at most once, when the future is /// completed. /// /// If you call any OnCompletion() method more than once, only the most recent /// callback you registered will be called. /// /// @param[in] callback Function or lambda to call. /// /// @note This method is not available when using STLPort on Android, as /// std::function is not supported on STLPort. /// /// @note This is the same callback as FutureBase::OnCompletion(), so you /// can't expect to set both and have both run; again, only the most recently /// registered one will run. void OnCompletion( std::function&)> callback) const { FutureBase::OnCompletion( *reinterpret_cast*>(&callback)); } #endif // defined(FIREBASE_USE_STD_FUNCTION) || defined(DOXYGEN) }; // NOLINTNEXTLINE - allow namespace overridden } // namespace firebase // Include the inline implementation. #include "firebase/internal/future_impl.h" #endif // FIREBASE_APP_CLIENT_CPP_SRC_INCLUDE_FIREBASE_FUTURE_H_