259 lines
7.3 KiB
C
259 lines
7.3 KiB
C
|
/*
|
||
|
* 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
|