Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "prerelease",
"comment": "Implemented support for native module std::weak_ptr",
"packageName": "react-native-windows",
"email": "[email protected]",
"dependentChangeType": "patch",
"date": "2020-05-21T20:01:24.822Z"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#include "DebugHelpers.h"
#include "NativeModules.h"

#define DEBUG_OUTPUT(...) DebugWriteLine("SampleModuleCppImpl", ##__VA_ARGS__);
#define DEBUG_OUTPUT(...) DebugWriteLine("SampleLibraryCpp", ##__VA_ARGS__);

namespace SampleLibraryCpp {

Expand Down Expand Up @@ -42,6 +42,7 @@ struct SampleModuleCppImpl {
const ReactNotificationId<int> cppTimerNotification{L"SampleModuleCppImpl", L"TimerNotification"};
const ReactNotificationId<int> csTimerNotification{L"SampleModuleCS", L"TimerNotification"};

// Note that all notification subscriptions are removed automatically when React instance is unloaded.
reactContext.Notifications().Subscribe(csTimerNotification, [
](winrt::Windows::Foundation::IInspectable const &, ReactNotificationArgs<int> const &args) noexcept {
DEBUG_OUTPUT("C++ module, C# timer:", *args.Data());
Expand Down Expand Up @@ -256,4 +257,50 @@ struct SampleModuleCppImpl {
static constexpr std::chrono::milliseconds TimedEventInterval{5000};
};

// SampleSharedCppModule shows how to inherited native modules from std::enable_shared_from_this
// to use weak_from_this() in event handlers. In this example we use notifications instead
// of events just to show case the std::weak_ptr use.
REACT_MODULE(SampleSharedCppModule);
struct SampleSharedCppModule : std::enable_shared_from_this<SampleSharedCppModule> {
using IInspectable = winrt::Windows::Foundation::IInspectable;

// The Initialize method is called when React instance loaded JavaScript and the module is ready to use.
REACT_INIT(Initialize)
void Initialize(React::ReactContext const &reactContext) noexcept {
const ReactNotificationId<int> cppTimerNotification{L"SampleModuleCppImpl", L"TimerNotification"};
const ReactNotificationId<int> csTimerNotification{L"SampleModuleCS", L"TimerNotification"};

// Note that all notification subscriptions are removed automatically when React instance is unloaded.
reactContext.Notifications().Subscribe(
csTimerNotification,
[weakSelf = weak_from_this()](IInspectable const &, ReactNotificationArgs<int> const &args) noexcept {
if (auto strongSelf = weakSelf.lock()) {
strongSelf->ReportCSTimer(*args.Data());
}
});

reactContext.Notifications().Subscribe(
csTimerNotification,
[weakSelf = weak_from_this()](IInspectable const &, ReactNotificationArgs<int> const &args) noexcept {
if (auto strongSelf = weakSelf.lock()) {
strongSelf->ReportCppTimer(*args.Data());
}
});
}

void ReportCSTimer(int timerCount) noexcept {
DEBUG_OUTPUT("SampleSharedCppModule, C# timer:", timerCount);
}

void ReportCppTimer(int timerCount) noexcept {
DEBUG_OUTPUT("SampleSharedCppModule, C++ timer:", timerCount);
}

REACT_METHOD(SayHello)
std::string SayHello() noexcept {
// This method is currently unused
return "Hello";
}
};

} // namespace SampleLibraryCpp
69 changes: 58 additions & 11 deletions vnext/Microsoft.ReactNative.Cxx/NativeModules.h
Original file line number Diff line number Diff line change
Expand Up @@ -1108,29 +1108,76 @@ struct TurboModuleSpec {
}
};

// The default factory for the TModule.
// It wraps up the TModule into a ReactNonAbiValue to be passed through the ABI boundary.
template <class TModule>
inline std::tuple<IInspectable, TModule *> MakeDefaultReactModuleWrapper() noexcept {
ReactNonAbiValue<TModule> moduleWrapper{std::in_place};
TModule *module = moduleWrapper.GetPtr();
return std::tuple<IInspectable, TModule *>{std::move(moduleWrapper), module};
}

// The default factory for TModule inherited from enable_shared_from_this<T>.
// It wraps up the TModule into an std::shared_ptr before giving it to ReactNonAbiValue.
template <class TModule>
inline std::tuple<IInspectable, TModule *> MakeDefaultSharedPtrReactModuleWrapper() noexcept {
ReactNonAbiValue<std::shared_ptr<TModule>> moduleWrapper{std::in_place, std::make_shared<TModule>()};
TModule *module = moduleWrapper.GetPtr()->get();
return std::tuple<IInspectable, TModule *>{std::move(moduleWrapper), module};
}

namespace Internal {
// Internal functions to test if type is inherited from std::enable_shared_from_this<T>.
template <class T>
std::true_type IsBaseOfTemplateTest(std::enable_shared_from_this<T> *);
std::false_type IsBaseOfTemplateTest(...);
} // namespace Internal

// Check if type T is inherited from std::enable_shared_form_this<U>.
// We support the scenario when the T and U are different types.
template <class T>
using IsEnabledSharedFromThisT = decltype(Internal::IsBaseOfTemplateTest((T *)nullptr));
template <class T>
inline constexpr bool IsEnabledSharedFromThisV = IsEnabledSharedFromThisT<T>::value;

// Default implementation of factory getter for a TModule type **not** inherited from std::enable_shared_form_this.
// For the custom implementation define GetReactModuleFactory with the last parameter to be 'int' (not 'int *').
template <class TModule, std::enable_if_t<!IsEnabledSharedFromThisV<TModule>, int> = 0>
inline constexpr auto GetReactModuleFactory(TModule * /*moduleNullPtr*/, int * /*useDefault*/) noexcept {
return &MakeDefaultReactModuleWrapper<TModule>;
}

// Default implementation of factory getter for a TModule type inherited from std::enable_shared_form_this.
// For the custom implementation define GetReactModuleFactory with the last parameter to be 'int' (not 'int *').
template <class TModule, std::enable_if_t<IsEnabledSharedFromThisV<TModule>, int> = 0>
inline constexpr auto GetReactModuleFactory(TModule * /*moduleNullPtr*/, int * /*useDefault*/) noexcept {
return &MakeDefaultSharedPtrReactModuleWrapper<TModule>;
}

// Type traits for TModule. It defines a factory to create the module and its ABI-safe wrapper.
template <class TModule>
struct ReactModuleTraits {
using FactoryType = std::tuple<IInspectable, TModule *>() noexcept;
static constexpr FactoryType *Factory = GetReactModuleFactory((TModule *)nullptr, 0);
};

// Create a module provider for TModule type.
template <class TModule>
inline ReactModuleProvider MakeModuleProvider() noexcept {
return [](IReactModuleBuilder const &moduleBuilder) noexcept {
ReactNonAbiValue<TModule> moduleObject{std::in_place};
TModule *module = moduleObject.GetPtr();
auto [moduleWrapper, module] = ReactModuleTraits<TModule>::Factory();
ReactModuleBuilder builder{module, moduleBuilder};
GetReactModuleInfo(module, builder);
builder.CompleteRegistration();
return moduleObject;
return moduleWrapper;
};
}

// Create a module provider for TModule type that satisfies the TModuleSpec.
template <class TModule, class TModuleSpec>
inline ReactModuleProvider MakeTurboModuleProvider() noexcept {
TModuleSpec::template ValidateModule<TModule>();
return [](IReactModuleBuilder const &moduleBuilder) noexcept {
ReactNonAbiValue<TModule> moduleObject{std::in_place};
TModule *module = moduleObject.GetPtr();
ReactModuleBuilder builder{module, moduleBuilder};
GetReactModuleInfo(module, builder);
builder.CompleteRegistration();
return moduleObject;
};
return MakeModuleProvider<TModule>();
}

} // namespace winrt::Microsoft::ReactNative
Expand Down