diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-14 23:17:14 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-14 23:17:14 +0000 |
commit | e10b2b28a02d87eb9903966efd944790e3fc2877 (patch) | |
tree | 2c5b2db4d2a5f6421d4a6177dbb1b2265fd8800e /ppapi/utility | |
parent | 88bbd811149fdbe63150a2c7ed86d575c40644a3 (diff) | |
download | chromium_src-e10b2b28a02d87eb9903966efd944790e3fc2877.zip chromium_src-e10b2b28a02d87eb9903966efd944790e3fc2877.tar.gz chromium_src-e10b2b28a02d87eb9903966efd944790e3fc2877.tar.bz2 |
Add C++ wrappers for output parameters.
Define helper routines for doing array output using the new PP_OutputArray
struct. Define routines in the completion callback factory for doing output
parameters as parameters to the callback function.
BUG=
TEST=
Review URL: http://codereview.chromium.org/9651002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126781 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ppapi/utility')
-rw-r--r-- | ppapi/utility/completion_callback_factory.h | 424 |
1 files changed, 374 insertions, 50 deletions
diff --git a/ppapi/utility/completion_callback_factory.h b/ppapi/utility/completion_callback_factory.h index e0b1f37..964cd02 100644 --- a/ppapi/utility/completion_callback_factory.h +++ b/ppapi/utility/completion_callback_factory.h @@ -10,6 +10,27 @@ namespace pp { +// TypeUnwrapper -------------------------------------------------------------- + +namespace internal { + +// The TypeUnwrapper converts references and const references to the +// underlying type used for storage and passing as an argument. It is for +// internal use only. +template <typename T> struct TypeUnwrapper { + typedef T StorageType; +}; +template <typename T> struct TypeUnwrapper<T&> { + typedef T StorageType; +}; +template <typename T> struct TypeUnwrapper<const T&> { + typedef T StorageType; +}; + +} // namespace internal + +// ---------------------------------------------------------------------------- + /// CompletionCallbackFactory<T> may be used to create CompletionCallback /// objects that are bound to member functions. /// @@ -30,68 +51,137 @@ namespace pp { /// <strong>Example: </strong> /// /// @code -/// -/// class MyHandler { +/// class MyClass { /// public: /// // If an compiler warns on following using |this| in the initializer /// // list, use PP_ALLOW_THIS_IN_INITIALIZER_LIST macro. -/// MyHandler() : factory_(this), offset_(0) { +/// MyClass() : factory_(this) { /// } /// -/// void ProcessFile(const FileRef& file) { -/// CompletionCallback cc = factory_.NewCallback( -/// &MyHandler::DidOpen); -/// int32_t rv = fio_.Open(file, PP_FileOpenFlag_Read, cc); +/// void OpenFile(const pp::FileRef& file) { +/// pp::CompletionCallback cc = factory_.NewCallback(&MyClass::DidOpen); +/// int32_t rv = file_io_.Open(file, PP_FileOpenFlag_Read, cc); /// CHECK(rv == PP_OK_COMPLETIONPENDING); /// } /// /// private: -/// CompletionCallback NewCallback() { -/// return factory_.NewCallback(&MyHandler::DidCompleteIO); -/// } -/// /// void DidOpen(int32_t result) { /// if (result == PP_OK) { /// // The file is open, and we can begin reading. -/// offset_ = 0; -/// ReadMore(); +/// // ... /// } else { /// // Failed to open the file with error given by 'result'. /// } /// } /// -/// void DidRead(int32_t result) { -/// if (result > 0) { -/// // buf_ now contains 'result' number of bytes from the file. -/// ProcessBytes(buf_, result); -/// offset_ += result; -/// ReadMore(); -/// } else { -/// // Done reading (possibly with an error given by 'result'). -/// } -/// } +/// pp::CompletionCallbackFactory<MyClass> factory_; +/// }; +/// @endcode /// -/// void ReadMore() { -/// CompletionCallback cc = -/// factory_.NewOptionalCallback(&MyHandler::DidRead); -/// int32_t rv = fio_.Read(offset_, buf_, sizeof(buf_), -/// cc.pp_completion_callback()); -/// if (rv != PP_OK_COMPLETIONPENDING) -/// cc.Run(rv); -/// } +/// <strong>Passing additional parameters to your callback</strong> /// -/// void ProcessBytes(const char* bytes, int32_t length) { -/// // Do work ... -/// } +/// As a convenience, the <code>CompletionCallbackFactory</code> can optionally +/// create a closure with up to three bound parameters that it will pass to +/// your callback function. This can be useful for passing information about +/// the request to your callback function, which is especially useful if your +/// class has multiple asynchronous callbacks pending. /// -/// pp::CompletionCallbackFactory<MyHandler> factory_; -/// pp::FileIO fio_; -/// char buf_[4096]; -/// int64_t offset_; -/// }; +/// For the above example, of opening a file, let's say you want to keep some +/// description associated with your request, you might implement your OpenFile +/// and DidOpen callback as follows: /// +/// @code +/// void OpenFile(const pp::FileRef& file) { +/// std::string message = "Opening file!"; +/// pp::CompletionCallback cc = factory_.NewCallback(&MyClass::DidOpen, +/// message); +/// int32_t rv = file_io_.Open(file, PP_FileOpenFlag_Read, cc); +/// CHECK(rv == PP_OK_COMPLETIONPENDING); +/// } +/// void DidOpen(int32_t result, const std::string& message) { +/// // "message" will be "Opening file!". +/// ... +/// } +/// @endcode +/// +/// <strong>Optional versus required callbacks</strong> +/// +/// When you create an "optional" callback, the browser may return the results +/// synchronously if they are available. This can allow for higher performance +/// in some cases if data is available quickly (for example, for network loads +/// where there may be a lot of data coming quickly). In this case, the +/// callback will never be run. +/// +/// When creating a new callback with the factory, there will be data allocated +/// on the heap that tracks the callback information and any bound arguments. +/// This data is freed when the callback executes. In the case of optional +/// callbacks, since the browser will never issue the callback, the internal +/// tracking data will be leaked. +/// +/// Therefore, if you use optional callbacks, it's important to manually +/// issue the callback to free up this data. The typical pattern is: +/// +/// @code +/// pp::CompletionCallback callback = callback_factory.NewOptionalCallback( +/// &MyClass::OnDataReady); +/// int32_t result = interface->GetData(callback); +/// if (result != PP_OK_COMPLETIONPENDING) +/// callback.Run(result); /// @endcode /// +/// Because of this additional complexity, it's generally recommended that +/// you not use optional callbacks except when performance is more important +/// (such as loading large resources from the network). In most other cases, +/// the performance difference will not be worth the additional complexity, +/// and most functions may never actually have the ability to complete +/// synchronously. +/// +/// <strong>Completion callbacks with output</strong> +/// +/// For some API calls, the browser returns data to the caller via an output +/// parameter. These can be difficult to manage since the output parameter +/// must remain valid for as long as the callback is pending. Note also that +/// CancelAll (or destroying the callback factory) does <i>not</i> cancel the +/// callback from the browser's perspective, only the execution of the callback +/// in the plugin code, and the output parameter will still be written to! +/// This means that you can't use class members as output parameters without +/// risking crashes. +/// +/// To make this case easier, the CompletionCallbackFactory can allocate and +/// manage the output data for you and pass it to your callback function. This +/// makes such calls more natural and less error-prone. +/// +/// To create such a callback, use NewCallbackWithOutput and specify a callback +/// function that takes the output parameter as its second argument. Let's say +/// you're calling a function GetFile which asynchronously returns a +/// pp::FileRef. GetFile's signature will be <code>int32_t GetFile(const +/// CompletionCallbackWithOutput<pp::FileRef>& callback);</code> and your +/// calling code would look like this: +/// +/// @code +/// void RequestFile() { +/// file_interface->GetFile(callback_factory_.NewCallbackWithOutput( +/// &MyClass::GotFile)); +/// } +/// void GotFile(int32_t result, const pp::FileRef& file) { +/// if (result == PP_OK) { +/// ...use file... +/// } else { +/// ...handle error... +/// } +/// } +/// @endcode +/// +/// As with regular completion callbacks, you can optionally add up to three +/// bound arguments. These are passed following the output argument. +/// +/// Your callback may take the output argument as a copy (common for small +/// types like integers, a const reference (common for structures and +/// resources to avoid an extra copy), or as a non-const reference. One +/// optimization you can do if your callback function may take large arrays +/// is to accept your output argument as a non-const reference and to swap() +/// the argument with a vector of your own to store it. This means you don't +/// have to copy the buffer to consume it. template <typename T, typename RefCount = NonThreadSafeRefCount> class CompletionCallbackFactory { public: @@ -155,7 +245,7 @@ class CompletionCallbackFactory { template <typename Method> CompletionCallback NewCallback(Method method) { PP_DCHECK(object_); - return NewCallbackHelper(Dispatcher0<Method>(method)); + return NewCallbackHelper(new Dispatcher0<Method>(method)); } /// NewOptionalCallback() allocates a new, single-use @@ -176,6 +266,25 @@ class CompletionCallbackFactory { return cc; } + /// NewCallbackWithOutput() allocates a new, single-use + /// <code>CompletionCallback</code> where the browser will pass an additional + /// parameter comtaining the result of the request. The + /// <code>CompletionCallback</code> must be run in order for the memory + /// allocated by the methods to be freed. + /// + /// @param[in] method The method to be invoked upon completion of the + /// operation. + /// + /// @return A <code>CompletionCallback</code>. + template <typename Output> + CompletionCallbackWithOutput< + typename internal::TypeUnwrapper<Output>::StorageType> + NewCallbackWithOutput(void (T::*method)(int32_t, Output)) { + return NewCallbackWithOutputHelper(new DispatcherWithOutput0< + typename internal::TypeUnwrapper<Output>::StorageType, + void (T::*)(int32_t, Output)>(method)); + } + /// NewCallback() allocates a new, single-use <code>CompletionCallback</code>. /// The <code>CompletionCallback</code> must be run in order for the memory /// allocated by the methods to be freed. @@ -191,7 +300,7 @@ class CompletionCallbackFactory { template <typename Method, typename A> CompletionCallback NewCallback(Method method, const A& a) { PP_DCHECK(object_); - return NewCallbackHelper(Dispatcher1<Method, A>(method, a)); + return NewCallbackHelper(new Dispatcher1<Method, A>(method, a)); } /// NewOptionalCallback() allocates a new, single-use @@ -216,6 +325,30 @@ class CompletionCallbackFactory { return cc; } + /// NewCallbackWithOutput() allocates a new, single-use + /// <code>CompletionCallback</code> where the browser will pass an additional + /// parameter comtaining the result of the request. The + /// <code>CompletionCallback</code> must be run in order for the memory + /// allocated by the methods to be freed. + /// + /// @param[in] method The method to be invoked upon completion of the + /// operation. + /// + /// @param[in] a Passed to <code>method</code> when the completion callback + /// runs. + /// + /// @return A <code>CompletionCallback</code>. + template <typename Output, typename A> + CompletionCallbackWithOutput< + typename internal::TypeUnwrapper<Output>::StorageType> + NewCallbackWithOutput(void (T::*method)(int32_t, Output, A), + const A& a) { + return NewCallbackWithOutputHelper(new DispatcherWithOutput1< + typename internal::TypeUnwrapper<Output>::StorageType, + void (T::*)(int32_t, Output, A), + typename internal::TypeUnwrapper<A>::StorageType>(method, a)); + } + /// NewCallback() allocates a new, single-use /// <code>CompletionCallback</code>. /// The <code>CompletionCallback</code> must be run in order for the memory @@ -234,7 +367,7 @@ class CompletionCallbackFactory { template <typename Method, typename A, typename B> CompletionCallback NewCallback(Method method, const A& a, const B& b) { PP_DCHECK(object_); - return NewCallbackHelper(Dispatcher2<Method, A, B>(method, a, b)); + return NewCallbackHelper(new Dispatcher2<Method, A, B>(method, a, b)); } /// NewOptionalCallback() allocates a new, single-use @@ -263,6 +396,35 @@ class CompletionCallbackFactory { return cc; } + /// NewCallbackWithOutput() allocates a new, single-use + /// <code>CompletionCallback</code> where the browser will pass an additional + /// parameter comtaining the result of the request. The + /// <code>CompletionCallback</code> must be run in order for the memory + /// allocated by the methods to be freed. + /// + /// @param[in] method The method to be invoked upon completion of the + /// operation. + /// + /// @param[in] a Passed to <code>method</code> when the completion callback + /// runs. + /// + /// @param[in] b Passed to <code>method</code> when the completion callback + /// runs. + /// + /// @return A <code>CompletionCallback</code>. + template <typename Output, typename A, typename B> + CompletionCallbackWithOutput< + typename internal::TypeUnwrapper<Output>::StorageType> + NewCallbackWithOutput(void (T::*method)(int32_t, Output, A, B), + const A& a, + const B& b) { + return NewCallbackWithOutputHelper(new DispatcherWithOutput2< + typename internal::TypeUnwrapper<Output>::StorageType, + void (T::*)(int32_t, Output, A, B), + typename internal::TypeUnwrapper<A>::StorageType, + typename internal::TypeUnwrapper<B>::StorageType>(method, a, b)); + } + /// NewCallback() allocates a new, single-use /// <code>CompletionCallback</code>. /// The <code>CompletionCallback</code> must be run in order for the memory @@ -287,7 +449,7 @@ class CompletionCallbackFactory { CompletionCallback NewCallback(Method method, const A& a, const B& b, const C& c) { PP_DCHECK(object_); - return NewCallbackHelper(Dispatcher3<Method, A, B, C>(method, a, b, c)); + return NewCallbackHelper(new Dispatcher3<Method, A, B, C>(method, a, b, c)); } /// NewOptionalCallback() allocates a new, single-use @@ -321,12 +483,45 @@ class CompletionCallbackFactory { return cc; } + /// NewCallbackWithOutput() allocates a new, single-use + /// <code>CompletionCallback</code> where the browser will pass an additional + /// parameter comtaining the result of the request. The + /// <code>CompletionCallback</code> must be run in order for the memory + /// allocated by the methods to be freed. + /// + /// @param method The method to be run. + /// + /// @param[in] a Passed to <code>method</code> when the completion callback + /// runs. + /// + /// @param[in] b Passed to <code>method</code> when the completion callback + /// runs. + /// + /// @param[in] c Passed to <code>method</code> when the completion callback + /// runs. + /// + /// @return A <code>CompletionCallback</code>. + template <typename Output, typename A, typename B, typename C> + CompletionCallbackWithOutput< + typename internal::TypeUnwrapper<Output>::StorageType> + NewCallbackWithOutput(void (T::*method)(int32_t, Output, A, B, C), + const A& a, + const B& b, + const C& c) { + return NewCallbackWithOutputHelper(new DispatcherWithOutput3< + typename internal::TypeUnwrapper<Output>::StorageType, + void (T::*)(int32_t, Output, A, B, C), + typename internal::TypeUnwrapper<A>::StorageType, + typename internal::TypeUnwrapper<B>::StorageType, + typename internal::TypeUnwrapper<C>::StorageType>(method, a, b, c)); + } + private: class BackPointer { public: typedef CompletionCallbackFactory<T, RefCount> FactoryType; - BackPointer(FactoryType* factory) + explicit BackPointer(FactoryType* factory) : factory_(factory) { } @@ -355,7 +550,8 @@ class CompletionCallbackFactory { template <typename Dispatcher> class CallbackData { public: - CallbackData(BackPointer* back_pointer, const Dispatcher& dispatcher) + // Takes ownership of the given dispatcher pointer. + CallbackData(BackPointer* back_pointer, Dispatcher* dispatcher) : back_pointer_(back_pointer), dispatcher_(dispatcher) { back_pointer_->AddRef(); @@ -363,26 +559,34 @@ class CompletionCallbackFactory { ~CallbackData() { back_pointer_->Release(); + delete dispatcher_; } + Dispatcher* dispatcher() { return dispatcher_; } + static void Thunk(void* user_data, int32_t result) { Self* self = static_cast<Self*>(user_data); T* object = self->back_pointer_->GetObject(); if (object) - self->dispatcher_(object, result); + (*self->dispatcher_)(object, result); delete self; } private: typedef CallbackData<Dispatcher> Self; - BackPointer* back_pointer_; - Dispatcher dispatcher_; + BackPointer* back_pointer_; // We own a ref to this refcounted object. + Dispatcher* dispatcher_; // We own this pointer. + + // Disallow copying & assignment. + CallbackData(const CallbackData<Dispatcher>&); + CallbackData<Dispatcher>& operator=(const CallbackData<Dispatcher>&); }; template <typename Method> class Dispatcher0 { public: - Dispatcher0(Method method) : method_(method) { + Dispatcher0() : method_(NULL) {} + explicit Dispatcher0(Method method) : method_(method) { } void operator()(T* object, int32_t result) { (object->*method_)(result); @@ -391,9 +595,31 @@ class CompletionCallbackFactory { Method method_; }; + template <typename Output, typename Method> + class DispatcherWithOutput0 { + public: + typedef Output OutputType; + typedef internal::CallbackOutputTraits<Output> Traits; + + DispatcherWithOutput0() : method_(NULL) {} + DispatcherWithOutput0(Method method) : method_(method) { + } + void operator()(T* object, int32_t result) { + (object->*method_)(result, Traits::StorageToPluginArg(output_)); + } + typename Traits::StorageType* output() { + return &output_; + } + private: + Method method_; + + typename Traits::StorageType output_; + }; + template <typename Method, typename A> class Dispatcher1 { public: + Dispatcher1() : method_(NULL) {} Dispatcher1(Method method, const A& a) : method_(method), a_(a) { @@ -406,9 +632,34 @@ class CompletionCallbackFactory { A a_; }; + template <typename Output, typename Method, typename A> + class DispatcherWithOutput1 { + public: + typedef Output OutputType; + typedef internal::CallbackOutputTraits<Output> Traits; + + DispatcherWithOutput1() : method_(NULL) {} + DispatcherWithOutput1(Method method, const A& a) + : method_(method), + a_(a) { + } + void operator()(T* object, int32_t result) { + (object->*method_)(result, Traits::StorageToPluginArg(output_), a_); + } + typename Traits::StorageType* output() { + return &output_; + } + private: + Method method_; + A a_; + + typename Traits::StorageType output_; + }; + template <typename Method, typename A, typename B> class Dispatcher2 { public: + Dispatcher2() : method_(NULL) {} Dispatcher2(Method method, const A& a, const B& b) : method_(method), a_(a), @@ -423,9 +674,36 @@ class CompletionCallbackFactory { B b_; }; + template <typename Output, typename Method, typename A, typename B> + class DispatcherWithOutput2 { + public: + typedef Output OutputType; + typedef internal::CallbackOutputTraits<Output> Traits; + + DispatcherWithOutput2() : method_(NULL) {} + DispatcherWithOutput2(Method method, const A& a, const B& b) + : method_(method), + a_(a), + b_(b) { + } + void operator()(T* object, int32_t result) { + (object->*method_)(result, Traits::StorageToPluginArg(output_), a_, b_); + } + typename Traits::StorageType* output() { + return &output_; + } + private: + Method method_; + A a_; + B b_; + + typename Traits::StorageType output_; + }; + template <typename Method, typename A, typename B, typename C> class Dispatcher3 { public: + Dispatcher3() : method_(NULL) {} Dispatcher3(Method method, const A& a, const B& b, const C& c) : method_(method), a_(a), @@ -442,6 +720,36 @@ class CompletionCallbackFactory { C c_; }; + template <typename Output, typename Method, typename A, typename B, + typename C> + class DispatcherWithOutput3 { + public: + typedef Output OutputType; + typedef internal::CallbackOutputTraits<Output> Traits; + + DispatcherWithOutput3() : method_(NULL) {} + DispatcherWithOutput3(Method method, const A& a, const B& b, const C& c) + : method_(method), + a_(a), + b_(b), + c_(c) { + } + void operator()(T* object, int32_t result) { + (object->*method_)(result, Traits::StorageToPluginArg(output_), + a_, b_, c_); + } + typename Traits::StorageType* output() { + return &output_; + } + private: + Method method_; + A a_; + B b_; + C c_; + + typename Traits::StorageType output_; + }; + void InitBackPointer() { back_pointer_ = new BackPointer(this); back_pointer_->AddRef(); @@ -452,14 +760,30 @@ class CompletionCallbackFactory { back_pointer_->Release(); } + // Takes ownership of the dispatcher pointer, which should be heap allocated. template <typename Dispatcher> - CompletionCallback NewCallbackHelper(const Dispatcher& dispatcher) { + CompletionCallback NewCallbackHelper(Dispatcher* dispatcher) { PP_DCHECK(object_); // Expects a non-null object! return CompletionCallback( &CallbackData<Dispatcher>::Thunk, new CallbackData<Dispatcher>(back_pointer_, dispatcher)); } + // Takes ownership of the dispatcher pointer, which should be heap allocated. + template <typename Dispatcher> CompletionCallbackWithOutput< + typename internal::TypeUnwrapper< + typename Dispatcher::OutputType>::StorageType> + NewCallbackWithOutputHelper(Dispatcher* dispatcher) { + PP_DCHECK(object_); // Expects a non-null object! + CallbackData<Dispatcher>* data = + new CallbackData<Dispatcher>(back_pointer_, dispatcher); + + return CompletionCallbackWithOutput<typename Dispatcher::OutputType>( + &CallbackData<Dispatcher>::Thunk, + data, + data->dispatcher()->output()); + } + // Disallowed: CompletionCallbackFactory(const CompletionCallbackFactory&); CompletionCallbackFactory& operator=(const CompletionCallbackFactory&); |