123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
- #pragma once
- #include <functional>
- #include <map>
- #include <tuple>
- #include <vector>
- #include <folly/dynamic.h>
- using namespace std::placeholders;
- namespace facebook {
- namespace react {
- class Instance;
- }
- } // namespace facebook
- namespace facebook {
- namespace xplat {
- namespace module {
- /**
- * Base class for Catalyst native modules whose implementations are
- * written in C++. Native methods are represented by instances of the
- * Method struct. Generally, a derived class will manage an instance
- * which represents the data for the module, and non-Catalyst-specific
- * methods can be wrapped in lambdas which convert between
- * folly::dynamic and native C++ objects. The Callback arguments will
- * pass through to js functions passed to the analogous javascript
- * methods. At most two callbacks will be converted. Results should
- * be passed to the first callback, and errors to the second callback.
- * Exceptions thrown by a method will be converted to platform
- * exceptions, and handled however they are handled on that platform.
- * (TODO mhorowitz #7128529: this exception behavior is not yet
- * implemented.)
- *
- * There are two sets of constructors here. The first set initializes
- * a Method using a name and anything convertible to a std::function.
- * This is most useful for registering a lambda as a RN method. There
- * are overloads to support functions which take no arguments,
- * arguments only, and zero, one, or two callbacks.
- *
- * The second set of methods is similar, but instead of taking a
- * function, takes the method name, an object, and a pointer to a
- * method on that object.
- */
- class CxxModule {
- class AsyncTagType {};
- class SyncTagType {};
- public:
- typedef std::function<std::unique_ptr<CxxModule>()> Provider;
- typedef std::function<void(std::vector<folly::dynamic>)> Callback;
- constexpr static AsyncTagType AsyncTag = AsyncTagType();
- constexpr static SyncTagType SyncTag = SyncTagType();
- struct Method {
- std::string name;
- size_t callbacks;
- bool isPromise;
- std::function<void(folly::dynamic, Callback, Callback)> func;
- std::function<folly::dynamic(folly::dynamic)> syncFunc;
- const char *getType() {
- assert(func || syncFunc);
- return func ? (isPromise ? "promise" : "async") : "sync";
- }
- // std::function/lambda ctors
- Method(std::string aname, std::function<void()> &&afunc)
- : name(std::move(aname)),
- callbacks(0),
- isPromise(false),
- func(std::bind(std::move(afunc))) {}
- Method(std::string aname, std::function<void(folly::dynamic)> &&afunc)
- : name(std::move(aname)),
- callbacks(0),
- isPromise(false),
- func(std::bind(std::move(afunc), std::placeholders::_1)) {}
- Method(
- std::string aname,
- std::function<void(folly::dynamic, Callback)> &&afunc)
- : name(std::move(aname)),
- callbacks(1),
- isPromise(false),
- func(std::bind(
- std::move(afunc),
- std::placeholders::_1,
- std::placeholders::_2)) {}
- Method(
- std::string aname,
- std::function<void(folly::dynamic, Callback, Callback)> &&afunc)
- : name(std::move(aname)),
- callbacks(2),
- isPromise(true),
- func(std::move(afunc)) {}
- Method(
- std::string aname,
- std::function<void(folly::dynamic, Callback, Callback)> &&afunc,
- AsyncTagType)
- : name(std::move(aname)),
- callbacks(2),
- isPromise(false),
- func(std::move(afunc)) {}
- // method pointer ctors
- template <typename T>
- Method(std::string aname, T *t, void (T::*method)())
- : name(std::move(aname)),
- callbacks(0),
- isPromise(false),
- func(std::bind(method, t)) {}
- template <typename T>
- Method(std::string aname, T *t, void (T::*method)(folly::dynamic))
- : name(std::move(aname)),
- callbacks(0),
- isPromise(false),
- func(std::bind(method, t, std::placeholders::_1)) {}
- template <typename T>
- Method(std::string aname, T *t, void (T::*method)(folly::dynamic, Callback))
- : name(std::move(aname)),
- callbacks(1),
- isPromise(false),
- func(std::bind(
- method,
- t,
- std::placeholders::_1,
- std::placeholders::_2)) {}
- template <typename T>
- Method(
- std::string aname,
- T *t,
- void (T::*method)(folly::dynamic, Callback, Callback))
- : name(std::move(aname)),
- callbacks(2),
- isPromise(true),
- func(std::bind(
- method,
- t,
- std::placeholders::_1,
- std::placeholders::_2,
- std::placeholders::_3)) {}
- template <typename T>
- Method(
- std::string aname,
- T *t,
- void (T::*method)(folly::dynamic, Callback, Callback),
- AsyncTagType)
- : name(std::move(aname)),
- callbacks(2),
- isPromise(false),
- func(std::bind(
- method,
- t,
- std::placeholders::_1,
- std::placeholders::_2,
- std::placeholders::_3)) {}
- // sync std::function/lambda ctors
- // Overloads for functions returning void give ambiguity errors.
- // I am not sure if this is a runtime/compiler bug, or a
- // limitation I do not understand.
- Method(
- std::string aname,
- std::function<folly::dynamic()> &&afunc,
- SyncTagType)
- : name(std::move(aname)),
- callbacks(0),
- isPromise(false),
- syncFunc([afunc = std::move(afunc)](const folly::dynamic &) {
- return afunc();
- }) {}
- Method(
- std::string aname,
- std::function<folly::dynamic(folly::dynamic)> &&afunc,
- SyncTagType)
- : name(std::move(aname)),
- callbacks(0),
- isPromise(false),
- syncFunc(std::move(afunc)) {}
- };
- /**
- * This may block, if necessary to complete cleanup before the
- * object is destroyed.
- */
- virtual ~CxxModule() {}
- /**
- * @return the name of this module. This will be the name used to {@code
- * require()} this module from javascript.
- */
- virtual std::string getName() = 0;
- /**
- * Each entry in the map will be exported as a property to JS. The
- * key is the property name, and the value can be anything.
- */
- virtual auto getConstants() -> std::map<std::string, folly::dynamic> {
- return {};
- };
- /**
- * @return a list of methods this module exports to JS.
- */
- virtual auto getMethods() -> std::vector<Method> = 0;
- /**
- * Called during the construction of CxxNativeModule.
- */
- void setInstance(std::weak_ptr<react::Instance> instance) {
- instance_ = instance;
- }
- /**
- * @return a weak_ptr to the current instance of the bridge.
- * When used with CxxNativeModule, this gives Cxx modules access to functions
- * such as `callJSFunction`, allowing them to communicate back to JS outside
- * of the regular callbacks.
- */
- std::weak_ptr<react::Instance> getInstance() {
- return instance_;
- }
- private:
- std::weak_ptr<react::Instance> instance_;
- };
- } // namespace module
- } // namespace xplat
- } // namespace facebook
|