diff --git a/change/react-native-windows-2020-04-10-13-52-12-SimplifyReactMacros.json b/change/react-native-windows-2020-04-10-13-52-12-SimplifyReactMacros.json new file mode 100644 index 00000000000..e56d9ac3678 --- /dev/null +++ b/change/react-native-windows-2020-04-10-13-52-12-SimplifyReactMacros.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Simplified C++ macros and improved their comments", + "packageName": "react-native-windows", + "email": "vmorozov@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-04-10T20:52:12.369Z" +} \ No newline at end of file diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/NoAttributeNativeModuleTest.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/NoAttributeNativeModuleTest.cpp index 45eaa2ecd48..149e39eec87 100644 --- a/vnext/Microsoft.ReactNative.Cxx.UnitTests/NoAttributeNativeModuleTest.cpp +++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/NoAttributeNativeModuleTest.cpp @@ -573,24 +573,24 @@ void RegisterModule(ReactModuleBuilder &moduleBuilder) noex moduleBuilder.RegisterSyncMethod(&SimpleNativeModule2::StaticAddSync, L"StaticAddSync"); moduleBuilder.RegisterSyncMethod(&SimpleNativeModule2::StaticNegateSync, L"StaticNegateSync"); moduleBuilder.RegisterSyncMethod(&SimpleNativeModule2::StaticSayHelloSync, L"StaticSayHelloSync"); - moduleBuilder.RegisterConstant(&SimpleNativeModule2::Constant1, L"Constant1"); - moduleBuilder.RegisterConstant(&SimpleNativeModule2::Constant2, L"const2"); - moduleBuilder.RegisterConstant(&SimpleNativeModule2::Constant3, L"const3"); - moduleBuilder.RegisterConstant(&SimpleNativeModule2::Constant4, L"Constant4"); - moduleBuilder.RegisterConstMethod(&SimpleNativeModule2::Constant5, L"Constant5"); - moduleBuilder.RegisterConstMethod(&SimpleNativeModule2::Constant6, L"Constant6"); - moduleBuilder.RegisterEvent(&SimpleNativeModule2::OnIntEvent, L"OnIntEvent"); - moduleBuilder.RegisterEvent(&SimpleNativeModule2::OnNoArgEvent, L"OnNoArgEvent"); - moduleBuilder.RegisterEvent(&SimpleNativeModule2::OnTwoArgsEvent, L"OnTwoArgsEvent"); - moduleBuilder.RegisterEvent(&SimpleNativeModule2::OnPointEvent, L"onPointEvent"); - moduleBuilder.RegisterEvent(&SimpleNativeModule2::OnStringEvent, L"onStringEvent", L"MyEventEmitter"); - moduleBuilder.RegisterEvent(&SimpleNativeModule2::OnJSValueEvent, L"OnJSValueEvent"); - moduleBuilder.RegisterFunction(&SimpleNativeModule2::JSIntFunction, L"JSIntFunction"); - moduleBuilder.RegisterFunction(&SimpleNativeModule2::JSPointFunction, L"pointFunc"); - moduleBuilder.RegisterFunction(&SimpleNativeModule2::JSLineFunction, L"lineFunc"); - moduleBuilder.RegisterFunction(&SimpleNativeModule2::JSNoArgFunction, L"JSNoArgFunction"); - moduleBuilder.RegisterFunction(&SimpleNativeModule2::JSStringFunction, L"stringFunc", L"MyModule"); - moduleBuilder.RegisterFunction(&SimpleNativeModule2::JSValueFunction, L"JSValueFunction"); + moduleBuilder.RegisterConstantField(&SimpleNativeModule2::Constant1, L"Constant1"); + moduleBuilder.RegisterConstantField(&SimpleNativeModule2::Constant2, L"const2"); + moduleBuilder.RegisterConstantField(&SimpleNativeModule2::Constant3, L"const3"); + moduleBuilder.RegisterConstantField(&SimpleNativeModule2::Constant4, L"Constant4"); + moduleBuilder.RegisterConstantMethod(&SimpleNativeModule2::Constant5, L"Constant5"); + moduleBuilder.RegisterConstantMethod(&SimpleNativeModule2::Constant6, L"Constant6"); + moduleBuilder.RegisterEventField(&SimpleNativeModule2::OnIntEvent, L"OnIntEvent"); + moduleBuilder.RegisterEventField(&SimpleNativeModule2::OnNoArgEvent, L"OnNoArgEvent"); + moduleBuilder.RegisterEventField(&SimpleNativeModule2::OnTwoArgsEvent, L"OnTwoArgsEvent"); + moduleBuilder.RegisterEventField(&SimpleNativeModule2::OnPointEvent, L"onPointEvent"); + moduleBuilder.RegisterEventField(&SimpleNativeModule2::OnStringEvent, L"onStringEvent", L"MyEventEmitter"); + moduleBuilder.RegisterEventField(&SimpleNativeModule2::OnJSValueEvent, L"OnJSValueEvent"); + moduleBuilder.RegisterFunctionField(&SimpleNativeModule2::JSIntFunction, L"JSIntFunction"); + moduleBuilder.RegisterFunctionField(&SimpleNativeModule2::JSPointFunction, L"pointFunc"); + moduleBuilder.RegisterFunctionField(&SimpleNativeModule2::JSLineFunction, L"lineFunc"); + moduleBuilder.RegisterFunctionField(&SimpleNativeModule2::JSNoArgFunction, L"JSNoArgFunction"); + moduleBuilder.RegisterFunctionField(&SimpleNativeModule2::JSStringFunction, L"stringFunc", L"MyModule"); + moduleBuilder.RegisterFunctionField(&SimpleNativeModule2::JSValueFunction, L"JSValueFunction"); } TEST_CLASS (NoAttributeNativeModuleTest) { diff --git a/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems b/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems index 7ba3d8d6089..58c293cb434 100644 --- a/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems +++ b/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems @@ -21,12 +21,9 @@ - - - diff --git a/vnext/Microsoft.ReactNative.Cxx/ModuleMemberRegistration.h b/vnext/Microsoft.ReactNative.Cxx/ModuleMemberRegistration.h deleted file mode 100644 index 004e717bd67..00000000000 --- a/vnext/Microsoft.ReactNative.Cxx/ModuleMemberRegistration.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -// We implement optional parameter macros based on the StackOverflow discussion: -// https://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros -// Please refer to it if you want to understand it works. - -#pragma once -#include "ReactMemberInfo.h" - -// Internal implementation details. -// For each registered member we create a static method that registers it. -// The member Id is generated as a ReactMemberId<__COUNTER__> type. -// To invoke the static registration methods, we increment ReactMemberId while static member exists. -#define INTERNAL_REACT_METHOD_3_ARGS(methodType, method, methodName) \ - template \ - static void RegisterMember( \ - TRegistry ®istry, winrt::Microsoft::ReactNative::ReactMemberId<__COUNTER__>) noexcept { \ - registry.Register##methodType##Method(&TClass::method, methodName); \ - } - -#define INTERNAL_REACT_METHOD_2_ARGS(methodType, method) INTERNAL_REACT_METHOD_3_ARGS(methodType, method, L## #method) - -#define INTERNAL_REACT_MEMBER_3RD_ARG(arg1, arg2, arg3, ...) arg3 -#define INTERNAL_REACT_MEMBER_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4 - -#define INTERNAL_REACT_MEMBER_RECOMPOSER2(argsWithParentheses) INTERNAL_REACT_MEMBER_3RD_ARG argsWithParentheses -#define INTERNAL_REACT_MEMBER_RECOMPOSER3(argsWithParentheses) INTERNAL_REACT_MEMBER_4TH_ARG argsWithParentheses - -#define INTERNAL_REACT_METHOD(...) \ - INTERNAL_REACT_MEMBER_RECOMPOSER2((__VA_ARGS__, INTERNAL_REACT_METHOD_3_ARGS, INTERNAL_REACT_METHOD_2_ARGS, )) - -#define INTERNAL_REACT_CONSTANT_2_ARGS(field, constantName) \ - template \ - static void RegisterMember( \ - TRegistry ®istry, winrt::Microsoft::ReactNative::ReactMemberId<__COUNTER__>) noexcept { \ - registry.RegisterConstant(&TClass::field, constantName); \ - } - -#define INTERNAL_REACT_CONSTANT_1_ARGS(field) INTERNAL_REACT_CONSTANT_2_ARGS(field, L## #field) - -#define INTERNAL_REACT_CONSTANT(...) \ - INTERNAL_REACT_MEMBER_RECOMPOSER2((__VA_ARGS__, INTERNAL_REACT_CONSTANT_2_ARGS, INTERNAL_REACT_CONSTANT_1_ARGS, )) - -#define INTERNAL_REACT_EVENT_3_ARGS(field, eventName, eventEmitterName) \ - template \ - static void RegisterMember( \ - TRegistry ®istry, winrt::Microsoft::ReactNative::ReactMemberId<__COUNTER__>) noexcept { \ - registry.RegisterEvent(&TClass::field, eventName, eventEmitterName); \ - } - -#define INTERNAL_REACT_EVENT_2_ARGS(field, eventName) \ - INTERNAL_REACT_EVENT_3_ARGS(field, eventName, /*defined by REACT_MODULE*/ nullptr) - -#define INTERNAL_REACT_EVENT_1_ARGS(field) INTERNAL_REACT_EVENT_2_ARGS(field, L## #field) - -#define INTERNAL_REACT_EVENT(...) \ - INTERNAL_REACT_MEMBER_RECOMPOSER3( \ - (__VA_ARGS__, INTERNAL_REACT_EVENT_3_ARGS, INTERNAL_REACT_EVENT_2_ARGS, INTERNAL_REACT_EVENT_1_ARGS, )) - -#define INTERNAL_REACT_FUNCTION_3_ARGS(field, functionName, moduleName) \ - template \ - static void RegisterMember( \ - TRegistry ®istry, winrt::Microsoft::ReactNative::ReactMemberId<__COUNTER__>) noexcept { \ - registry.RegisterFunction(&TClass::field, functionName, moduleName); \ - } - -#define INTERNAL_REACT_FUNCTION_2_ARGS(field, functionName) \ - INTERNAL_REACT_FUNCTION_3_ARGS(field, functionName, /*defined by REACT_MODULE*/ nullptr) - -#define INTERNAL_REACT_FUNCTION_1_ARGS(field) INTERNAL_REACT_FUNCTION_2_ARGS(field, L## #field) - -#define INTERNAL_REACT_FUNCTION(...) \ - INTERNAL_REACT_MEMBER_RECOMPOSER3( \ - (__VA_ARGS__, INTERNAL_REACT_FUNCTION_3_ARGS, INTERNAL_REACT_FUNCTION_2_ARGS, INTERNAL_REACT_FUNCTION_1_ARGS, )) diff --git a/vnext/Microsoft.ReactNative.Cxx/ModuleRegistration.h b/vnext/Microsoft.ReactNative.Cxx/ModuleRegistration.h index e1d4ab577a5..03916ce88e4 100644 --- a/vnext/Microsoft.ReactNative.Cxx/ModuleRegistration.h +++ b/vnext/Microsoft.ReactNative.Cxx/ModuleRegistration.h @@ -4,6 +4,17 @@ #pragma once #include "winrt/Microsoft.ReactNative.h" +// We implement optional parameter macros based on the StackOverflow discussion: +// https://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros +// Please refer to it if you want to understand it works. +#define INTERNAL_REACT_GET_ARG_4(arg1, arg2, arg3, arg4, ...) arg4 +#define INTERNAL_REACT_RECOMPOSER_4(argsWithParentheses) INTERNAL_REACT_GET_ARG_4 argsWithParentheses + +// +// The macros below are internal implementation details for macro defined in nativeModules.h +// + +// Register struct as a ReactNative module. #define INTERNAL_REACT_MODULE_3_ARGS(moduleStruct, moduleName, eventEmitterName) \ struct moduleStruct; \ \ @@ -23,7 +34,7 @@ template struct moduleStruct##_ModuleRegistration; \ \ template \ - void RegisterModule(TRegistry ®istry) noexcept { \ + constexpr void RegisterModule(TRegistry ®istry) noexcept { \ registry.RegisterModule( \ moduleName, eventEmitterName, winrt::Microsoft::ReactNative::ReactMemberId<__COUNTER__>{}); \ } @@ -31,15 +42,31 @@ #define INTERNAL_REACT_MODULE_2_ARGS(moduleStruct, moduleName) \ INTERNAL_REACT_MODULE_3_ARGS(moduleStruct, moduleName, nullptr) -#define INTERNAL_REACT_MODULE_1_ARGS(moduleStruct) INTERNAL_REACT_MODULE_2_ARGS(moduleStruct, L## #moduleStruct) +#define INTERNAL_REACT_MODULE_1_ARG(moduleStruct) INTERNAL_REACT_MODULE_2_ARGS(moduleStruct, L## #moduleStruct) + +#define INTERNAL_REACT_MODULE(...) \ + INTERNAL_REACT_RECOMPOSER_4( \ + (__VA_ARGS__, INTERNAL_REACT_MODULE_3_ARGS, INTERNAL_REACT_MODULE_2_ARGS, INTERNAL_REACT_MODULE_1_ARG, )) + +// Register struct member. +// For each registered member we create a static method that registers it. +// The member Id is generated as a ReactMemberId<__COUNTER__> type. +// To invoke the static registration methods, we increment ReactMemberId while static member exists. +#define INTERNAL_REACT_MEMBER_4_ARGS(memberType, member, memberName, moduleName) \ + template \ + constexpr static void RegisterMember( \ + TRegistry ®istry, winrt::Microsoft::ReactNative::ReactMemberId<__COUNTER__>) noexcept { \ + registry.Register##memberType(&TClass::member, memberName, moduleName); \ + } -#define INTERNAL_REACT_MODULE_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4 +#define INTERNAL_REACT_MEMBER_3_ARGS(memberType, member, memberName) \ + INTERNAL_REACT_MEMBER_4_ARGS(memberType, member, memberName, nullptr) -#define INTERNAL_REACT_MODULE_RECOMPOSER(argsWithParentheses) INTERNAL_REACT_MODULE_4TH_ARG argsWithParentheses +#define INTERNAL_REACT_MEMBER_2_ARGS(memberType, member) INTERNAL_REACT_MEMBER_3_ARGS(memberType, member, L## #member) -#define INTERNAL_REACT_MODULE(...) \ - INTERNAL_REACT_MODULE_RECOMPOSER( \ - (__VA_ARGS__, INTERNAL_REACT_MODULE_3_ARGS, INTERNAL_REACT_MODULE_2_ARGS, INTERNAL_REACT_MODULE_1_ARGS, )) +#define INTERNAL_REACT_MEMBER(...) \ + INTERNAL_REACT_RECOMPOSER_4( \ + (__VA_ARGS__, INTERNAL_REACT_MEMBER_4_ARGS, INTERNAL_REACT_MEMBER_3_ARGS, INTERNAL_REACT_MEMBER_2_ARGS, )) namespace winrt::Microsoft::ReactNative { diff --git a/vnext/Microsoft.ReactNative.Cxx/ModuleRegistry.h b/vnext/Microsoft.ReactNative.Cxx/ModuleRegistry.h deleted file mode 100644 index 10a8e3bb95a..00000000000 --- a/vnext/Microsoft.ReactNative.Cxx/ModuleRegistry.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "ReactMemberInfo.h" - -namespace winrt::Microsoft::ReactNative { - -struct ModuleRegistry { - template - void RegisterModule(wchar_t const *moduleName, wchar_t const *eventEmitterName, ReactMemberId) noexcept; -}; - -} // namespace winrt::Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative.Cxx/NativeModules.h b/vnext/Microsoft.ReactNative.Cxx/NativeModules.h index 9653e144ade..305709f3aa6 100644 --- a/vnext/Microsoft.ReactNative.Cxx/NativeModules.h +++ b/vnext/Microsoft.ReactNative.Cxx/NativeModules.h @@ -4,33 +4,40 @@ #pragma once #include "winrt/Microsoft.ReactNative.h" -#include #include "JSValueReader.h" #include "JSValueWriter.h" -#include "ModuleMemberRegistration.h" #include "ModuleRegistration.h" #include "ReactPromise.h" -// REACT_MODULE annotates a C++ struct as a ReactNative module. -// It can be any struct which can be instantiated using a default constructor. -// Note that it must be a 'struct', not 'class' because macro does a forward declaration using the 'struct' keyword. -// +#include + +// REACT_MODULE(moduleStruct, [opt] moduleName, [opt] eventEmitterName) // Arguments: // - moduleStruct (required) - the struct name the macro is attached to. // - moduleName (optional) - the module name visible to JavaScript. Default is the moduleStruct name. // - eventEmitterName (optional) - the default event emitter name used by REACT_EVENT. // Default is the RCTDeviceEventEmitter. +// +// REACT_MODULE annotates a C++ struct as a ReactNative module. +// It can be any struct which can be instantiated using a default constructor. +// Note that it must be a 'struct', not 'class' because macro does a forward declaration using the 'struct' keyword. #define REACT_MODULE(/* moduleStruct, [opt] moduleName, [opt] eventEmitterName */...) \ INTERNAL_REACT_MODULE(__VA_ARGS__)(__VA_ARGS__) +// REACT_INIT(method) +// Arguments: +// - method (required) - the method name the macro is attached to. +// // REACT_INIT annotates a method that is called when a native module is initialized. // It must have 'IReactContext const &' parameter. // It must be an instance method. -// +#define REACT_INIT(method) INTERNAL_REACT_MEMBER_2_ARGS(InitMethod, method) + +// REACT_METHOD(method, [opt] methodName) // Arguments: // - method (required) - the method name the macro is attached to. -#define REACT_INIT(method) INTERNAL_REACT_METHOD_3_ARGS(Init, method, L"") - +// - methodName (optional) - the method name visible to JavaScript. Default is the method name. +// // REACT_METHOD annotates a method to export to JavaScript. // It declares an asynchronous method. To return a value: // - Return void and have a Callback as a last parameter. The Callback type can be any std::function like type. E.g. @@ -40,61 +47,63 @@ // - Return non-void value. In JavaScript it is treated as a method with one Callback. Return std::pair to // be able to communicate error condition. // It can be an instance or static method. -// +#define REACT_METHOD(/* method, [opt] methodName */...) INTERNAL_REACT_MEMBER(__VA_ARGS__)(Method, __VA_ARGS__) + +// REACT_SYNC_METHOD(method, [opt] methodName) // Arguments: // - method (required) - the method name the macro is attached to. // - methodName (optional) - the method name visible to JavaScript. Default is the method name. -#define REACT_METHOD(/* method, [opt] methodName */...) INTERNAL_REACT_METHOD(__VA_ARGS__)(, __VA_ARGS__) - +// // REACT_SYNC_METHOD annotates a method that is called synchronously. // It must be used rarely because it may cause out-of-order execution when used along with asynchronous methods. // The method must return non-void value type. // It can be an instance or static method. -// +#define REACT_SYNC_METHOD(/* method, [opt] methodName */...) INTERNAL_REACT_MEMBER(__VA_ARGS__)(SyncMethod, __VA_ARGS__) + +// REACT_CONSTANT_PROVIDER(method) // Arguments: // - method (required) - the method name the macro is attached to. -// - methodName (optional) - the method name visible to JavaScript. Default is the method name. -#define REACT_SYNC_METHOD(/* method, [opt] methodName */...) INTERNAL_REACT_METHOD(__VA_ARGS__)(Sync, __VA_ARGS__) - +// // REACT_CONSTANT_PROVIDER annotates a method that defines constants. // It must have 'ReactConstantProvider &' parameter. // It can be an instance or static method. -// -// Arguments: -// - method (required) - the method name the macro is attached to. -#define REACT_CONSTANT_PROVIDER(method) INTERNAL_REACT_METHOD_3_ARGS(Const, method, L"") +#define REACT_CONSTANT_PROVIDER(method) INTERNAL_REACT_MEMBER_2_ARGS(ConstantMethod, method) -// REACT_CONSTANT annotates a field that defines a constant. -// It can be an instance or static field. -// +// REACT_CONSTANT(field, [opt] constantName) // Arguments: // - field (required) - the field name the macro is attached to. // - constantName (optional) - the constant name visible to JavaScript. Default is the field name. -#define REACT_CONSTANT(/* field, [opt] constantName */...) INTERNAL_REACT_CONSTANT(__VA_ARGS__)(__VA_ARGS__) - -// REACT_EVENT annotates a field that helps raise a JavaScript event. -// The field type can be any std::function like type. E.g. Func. -// It must be an instance field. // +// REACT_CONSTANT annotates a field that defines a constant. +// It can be an instance or static field. +#define REACT_CONSTANT(/* field, [opt] constantName */...) \ + INTERNAL_REACT_MEMBER(__VA_ARGS__)(ConstantField, __VA_ARGS__) + +// REACT_EVENT(field, [opt] eventName, [opt] eventEmitterName) // Arguments: // - field (required) - the field name the macro is attached to. // - eventName (optional) - the JavaScript event name. Default is the field name. // - eventEmitterName (optional) - the JavaScript module event emitter name. Default is module's eventEmitterName which // is by default 'RCTDeviceEventEmitter'. -#define REACT_EVENT(/* field, [opt] eventName, [opt] eventEmitterName */...) \ - INTERNAL_REACT_EVENT(__VA_ARGS__)(__VA_ARGS__) - -// REACT_FUNCTION annotates a field that helps calling a JavaScript function. +// +// REACT_EVENT annotates a field that helps raise a JavaScript event. // The field type can be any std::function like type. E.g. Func. // It must be an instance field. -// +#define REACT_EVENT(/* field, [opt] eventName, [opt] eventEmitterName */...) \ + INTERNAL_REACT_MEMBER(__VA_ARGS__)(EventField, __VA_ARGS__) + +// REACT_FUNCTION(field, [opt] functionName, [opt] moduleName) // Arguments: // - field (required) - the field name the macro is attached to. // - functionName (optional) - the JavaScript function name. Default is the field name. // - moduleName (optional) - the JavaScript module name. Default is module's moduleName which is by default the class // name. +// +// REACT_FUNCTION annotates a field that helps calling a JavaScript function. +// The field type can be any std::function like type. E.g. Func. +// It must be an instance field. #define REACT_FUNCTION(/* field, [opt] functionName, [opt] moduleName */...) \ - INTERNAL_REACT_FUNCTION(__VA_ARGS__)(__VA_ARGS__) + INTERNAL_REACT_MEMBER(__VA_ARGS__)(FunctionField, __VA_ARGS__) namespace winrt::Microsoft::ReactNative { @@ -110,32 +119,19 @@ template