Fabric: Simplified way to specialize ComponentName in ConcreteShadowNode class template

Summary:
@public
Previously, all ConcreteShadowNode subclasses had to override `getComponentName()` function to specialize a name of the component. And often it was all that those subclasses do. Now, it's a template argument; and many ShadowNode classes can be created as oneliners via *just* specializing  ConcreteShadowNode template.

Unfortunately, C++ does not allow to use `std::string`s or string literals as template arguments, but it allows to use pointers. Moreover, those pointers must point to some linked data, hence, those values must be declared in .cpp (not .h) files. For simplicity, we put those constants in Props classes, (but this is not a strong requirement).

Reviewed By: mdvacca

Differential Revision: D8942826

fbshipit-source-id: 4fd517e2485eb8f8c20a51df9b3496941856d8a5
This commit is contained in:
Valentin Shergin 2018-08-04 09:30:13 -07:00 committed by Facebook Github Bot
parent 88293d391a
commit 67a79010ca
32 changed files with 118 additions and 166 deletions

View File

@ -5,14 +5,12 @@
* LICENSE file in the root directory of this source tree.
*/
#include <fabric/components/activityindicator/ActivityIndicatorViewShadowNode.h>
#include "ActivityIndicatorViewShadowNode.h"
namespace facebook {
namespace react {
ComponentName ActivityIndicatorViewShadowNode::getComponentName() const {
return ComponentName("ActivityIndicatorView");
}
const char ActivityIndicatorViewComponentName[] = "ActivityIndicatorView";
} // namespace react
} // namespace facebook

View File

@ -13,18 +13,16 @@
namespace facebook {
namespace react {
extern const char ActivityIndicatorViewComponentName[];
/*
* `ShadowNode` for <ActivityIndicatorView> component.
*/
class ActivityIndicatorViewShadowNode final:
public ConcreteViewShadowNode<ActivityIndicatorViewProps> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
ComponentName getComponentName() const override;
};
using ActivityIndicatorViewShadowNode =
ConcreteViewShadowNode<
ActivityIndicatorViewComponentName,
ActivityIndicatorViewProps
>;
} // namespace react
} // namespace facebook

View File

@ -14,9 +14,7 @@
namespace facebook {
namespace react {
ComponentName ImageShadowNode::getComponentName() const {
return ComponentName("Image");
}
const char ImageComponentName[] = "Image";
void ImageShadowNode::setImageManager(const SharedImageManager &imageManager) {
ensureUnsealed();

View File

@ -16,18 +16,22 @@
namespace facebook {
namespace react {
extern const char ImageComponentName[];
/*
* `ShadowNode` for <Image> component.
*/
class ImageShadowNode final:
public ConcreteViewShadowNode<ImageProps, ImageEventEmitter> {
public ConcreteViewShadowNode<
ImageComponentName,
ImageProps,
ImageEventEmitter
> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
ComponentName getComponentName() const override;
/*
* Associates a shared `ImageManager` with the node.
*/

View File

@ -24,7 +24,6 @@ class RootProps final:
public ViewProps {
public:
RootProps() = default;
RootProps(
const RootProps &sourceProps,

View File

@ -12,9 +12,7 @@
namespace facebook {
namespace react {
ComponentName RootShadowNode::getComponentName() const {
return ComponentName("RootView");
}
const char RootComponentName[] = "RootView";
void RootShadowNode::layout() {
ensureUnsealed();

View File

@ -21,6 +21,8 @@ class RootShadowNode;
using SharedRootShadowNode = std::shared_ptr<const RootShadowNode>;
using UnsharedRootShadowNode = std::shared_ptr<RootShadowNode>;
extern const char RootComponentName[];
/*
* `ShadowNode` for the root component.
* Besides all functionality of the `View` component, `RootShadowNode` contains
@ -28,14 +30,15 @@ using UnsharedRootShadowNode = std::shared_ptr<RootShadowNode>;
* shadow tree.
*/
class RootShadowNode final:
public ConcreteViewShadowNode<RootProps> {
public ConcreteViewShadowNode<
RootComponentName,
RootProps
> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
ComponentName getComponentName() const override;
/*
* Layouts the shadow tree.
*/

View File

@ -14,9 +14,7 @@
namespace facebook {
namespace react {
ComponentName ScrollViewShadowNode::getComponentName() const {
return ComponentName("ScrollView");
}
const char ScrollViewComponentName[] = "ScrollView";
void ScrollViewShadowNode::updateLocalData() {
ensureUnsealed();

View File

@ -7,8 +7,6 @@
#pragma once
#include <memory>
#include <fabric/components/scrollview/ScrollViewEventEmitter.h>
#include <fabric/components/scrollview/ScrollViewProps.h>
#include <fabric/components/view/ConcreteViewShadowNode.h>
@ -17,22 +15,22 @@
namespace facebook {
namespace react {
class ScrollViewShadowNode;
using SharedScrollViewShadowNode = std::shared_ptr<const ScrollViewShadowNode>;
extern const char ScrollViewComponentName[];
/*
* `ShadowNode` for <ScrollView> component.
*/
class ScrollViewShadowNode final:
public ConcreteViewShadowNode<ScrollViewProps, ScrollViewEventEmitter> {
public ConcreteViewShadowNode<
ScrollViewComponentName,
ScrollViewProps,
ScrollViewEventEmitter
> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
ComponentName getComponentName() const override;
#pragma mark - LayoutableShadowNode
void layout(LayoutContext layoutContext) override;

View File

@ -5,14 +5,12 @@
* LICENSE file in the root directory of this source tree.
*/
#include <fabric/components/switch/SwitchShadowNode.h>
#include "SwitchShadowNode.h"
namespace facebook {
namespace react {
ComponentName SwitchShadowNode::getComponentName() const {
return ComponentName("Switch");
}
extern const char SwitchComponentName[] = "Switch";
} // namespace react
} // namespace facebook

View File

@ -14,18 +14,17 @@
namespace facebook {
namespace react {
extern const char SwitchComponentName[];
/*
* `ShadowNode` for <Switch> component.
*/
class SwitchShadowNode final:
public ConcreteViewShadowNode<SwitchProps, SwitchEventEmitter> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
ComponentName getComponentName() const override;
};
using SwitchShadowNode =
ConcreteViewShadowNode<
SwitchComponentName,
SwitchProps,
SwitchEventEmitter
>;
} // namespace react
} // namespace facebook

View File

@ -24,7 +24,7 @@ AttributedString BaseTextShadowNode::getAttributedString(
for (const auto &childNode : *childNodes) {
// RawShadowNode
SharedRawTextShadowNode rawTextShadowNode = std::dynamic_pointer_cast<const RawTextShadowNode>(childNode);
auto rawTextShadowNode = std::dynamic_pointer_cast<const RawTextShadowNode>(childNode);
if (rawTextShadowNode) {
AttributedString::Fragment fragment;
fragment.string = rawTextShadowNode->getProps()->text;
@ -34,7 +34,7 @@ AttributedString BaseTextShadowNode::getAttributedString(
}
// TextShadowNode
SharedTextShadowNode textShadowNode = std::dynamic_pointer_cast<const TextShadowNode>(childNode);
auto textShadowNode = std::dynamic_pointer_cast<const TextShadowNode>(childNode);
if (textShadowNode) {
TextAttributes localTextAttributes = textAttributes;
localTextAttributes.apply(textShadowNode->getProps()->textAttributes);

View File

@ -30,7 +30,7 @@ public:
}
void adopt(UnsharedShadowNode shadowNode) const override {
ConcreteComponentDescriptor<ParagraphShadowNode>::adopt(shadowNode);
ConcreteComponentDescriptor::adopt(shadowNode);
assert(std::dynamic_pointer_cast<ParagraphShadowNode>(shadowNode));
auto paragraphShadowNode = std::static_pointer_cast<ParagraphShadowNode>(shadowNode);

View File

@ -18,10 +18,6 @@
namespace facebook {
namespace react {
class ParagraphProps;
using SharedParagraphProps = std::shared_ptr<const ParagraphProps>;
/*
* Props of <Paragraph> component.
* Most of the props are directly stored in composed `ParagraphAttributes`

View File

@ -7,16 +7,12 @@
#include "ParagraphShadowNode.h"
#include <fabric/debug/DebugStringConvertibleItem.h>
#import "ParagraphLocalData.h"
#include "ParagraphLocalData.h"
namespace facebook {
namespace react {
ComponentName ParagraphShadowNode::getComponentName() const {
return ComponentName("Paragraph");
}
const char ParagraphComponentName[] = "Paragraph";
AttributedString ParagraphShadowNode::getAttributedString() const {
if (!cachedAttributedString_.has_value()) {
@ -53,7 +49,7 @@ Size ParagraphShadowNode::measure(LayoutConstraints layoutConstraints) const {
void ParagraphShadowNode::layout(LayoutContext layoutContext) {
updateLocalData();
ConcreteViewShadowNode<ParagraphProps>::layout(layoutContext);
ConcreteViewShadowNode::layout(layoutContext);
}
} // namespace react

View File

@ -19,9 +19,7 @@
namespace facebook {
namespace react {
class ParagraphShadowNode;
using SharedParagraphShadowNode = std::shared_ptr<const ParagraphShadowNode>;
extern const char ParagraphComponentName[];
/*
* `ShadowNode` for <Paragraph> component, represents <View>-like component
@ -29,15 +27,16 @@ using SharedParagraphShadowNode = std::shared_ptr<const ParagraphShadowNode>;
* and <RawText> components.
*/
class ParagraphShadowNode:
public ConcreteViewShadowNode<ParagraphProps>,
public ConcreteViewShadowNode<
ParagraphComponentName,
ParagraphProps
>,
public BaseTextShadowNode {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
ComponentName getComponentName() const override;
/*
* Returns a `AttributedString` which represents text content of the node.
*/

View File

@ -23,7 +23,6 @@ class RawTextProps:
public Props {
public:
RawTextProps() = default;
RawTextProps(const RawTextProps &sourceProps, const RawProps &rawProps);

View File

@ -7,14 +7,10 @@
#include "RawTextShadowNode.h"
#include <fabric/debug/DebugStringConvertibleItem.h>
namespace facebook {
namespace react {
ComponentName RawTextShadowNode::getComponentName() const {
return ComponentName("RawText");
}
extern const char RawTextComponentName[] = "RawText";
} // namespace react
} // namespace facebook

View File

@ -7,18 +7,13 @@
#pragma once
#include <memory>
#include <fabric/components/text/RawTextProps.h>
#include <fabric/core/ConcreteShadowNode.h>
#include <fabric/core/ShadowNode.h>
namespace facebook {
namespace react {
class RawTextShadowNode;
using SharedRawTextShadowNode = std::shared_ptr<const RawTextShadowNode>;
extern const char RawTextComponentName[];
/*
* `ShadowNode` for <RawText> component, represents a purely regular string
@ -26,14 +21,11 @@ using SharedRawTextShadowNode = std::shared_ptr<const RawTextShadowNode>;
* is represented as `<RawText text="Hello!"/>`.
* <RawText> component must not have any children.
*/
class RawTextShadowNode:
public ConcreteShadowNode<RawTextProps> {
public:
using ConcreteShadowNode::ConcreteShadowNode;
ComponentName getComponentName() const override;
};
using RawTextShadowNode =
ConcreteShadowNode<
RawTextComponentName,
RawTextProps
>;
} // namespace react
} // namespace facebook

View File

@ -16,16 +16,11 @@
namespace facebook {
namespace react {
class TextProps;
using SharedTextProps = std::shared_ptr<const TextProps>;
class TextProps:
public Props,
public BaseTextProps {
public:
TextProps() = default;
TextProps(const TextProps &sourceProps, const RawProps &rawProps);

View File

@ -7,14 +7,10 @@
#include "TextShadowNode.h"
#include <fabric/debug/DebugStringConvertibleItem.h>
namespace facebook {
namespace react {
ComponentName TextShadowNode::getComponentName() const {
return ComponentName("Text");
}
extern const char TextComponentName[] = "Text";
} // namespace react
} // namespace facebook

View File

@ -7,30 +7,25 @@
#pragma once
#include <fabric/attributedstring/AttributedString.h>
#include <fabric/attributedstring/TextAttributes.h>
#include <fabric/components/text/BaseTextShadowNode.h>
#include <fabric/components/text/TextProps.h>
#include <fabric/components/text/TextShadowNode.h>
#include <fabric/core/ConcreteShadowNode.h>
#include <fabric/core/ShadowNode.h>
namespace facebook {
namespace react {
class TextShadowNode;
using SharedTextShadowNode = std::shared_ptr<const TextShadowNode>;
extern const char TextComponentName[];
class TextShadowNode:
public ConcreteShadowNode<TextProps>,
public ConcreteShadowNode<
TextComponentName,
TextProps
>,
public BaseTextShadowNode {
public:
using ConcreteShadowNode::ConcreteShadowNode;
ComponentName getComponentName() const override;
};
} // namespace react

View File

@ -24,9 +24,17 @@ namespace react {
* as <View> and similar basic behaviour).
* For example: <Paragraph>, <Image>, but not <Text>, <RawText>.
*/
template <typename ViewPropsT = ViewProps, typename ViewEventEmitterT = ViewEventEmitter>
template <
const char *concreteComponentName,
typename ViewPropsT = ViewProps,
typename ViewEventEmitterT = ViewEventEmitter
>
class ConcreteViewShadowNode:
public ConcreteShadowNode<ViewPropsT, ViewEventEmitterT>,
public ConcreteShadowNode<
concreteComponentName,
ViewPropsT,
ViewEventEmitterT
>,
public AccessibleShadowNode,
public YogaLayoutableShadowNode {
@ -50,7 +58,7 @@ public:
const SharedShadowNodeSharedList &children,
const ShadowNodeCloneFunction &cloneFunction
):
ConcreteShadowNode<ViewPropsT, ViewEventEmitterT>(
ConcreteShadowNode<concreteComponentName, ViewPropsT, ViewEventEmitterT>(
tag,
rootTag,
props,
@ -64,7 +72,7 @@ public:
YogaLayoutableShadowNode() {
YogaLayoutableShadowNode::setProps(*props);
YogaLayoutableShadowNode::setChildren(ConcreteShadowNode<ViewPropsT, ViewEventEmitterT>::template getChildrenSlice<YogaLayoutableShadowNode>());
YogaLayoutableShadowNode::setChildren(ConcreteShadowNode<concreteComponentName, ViewPropsT, ViewEventEmitterT>::template getChildrenSlice<YogaLayoutableShadowNode>());
};
ConcreteViewShadowNode(
@ -73,7 +81,7 @@ public:
const SharedConcreteViewEventEmitter &eventEmitter,
const SharedShadowNodeSharedList &children
):
ConcreteShadowNode<ViewPropsT, ViewEventEmitterT>(
ConcreteShadowNode<concreteComponentName, ViewPropsT, ViewEventEmitterT>(
shadowNode,
props,
eventEmitter,
@ -92,7 +100,7 @@ public:
}
if (children) {
YogaLayoutableShadowNode::setChildren(ConcreteShadowNode<ViewPropsT, ViewEventEmitterT>::template getChildrenSlice<YogaLayoutableShadowNode>());
YogaLayoutableShadowNode::setChildren(ConcreteShadowNode<concreteComponentName, ViewPropsT, ViewEventEmitterT>::template getChildrenSlice<YogaLayoutableShadowNode>());
}
};

View File

@ -27,7 +27,6 @@ class ViewProps:
public AccessibilityProps {
public:
ViewProps() = default;
ViewProps(const YGStyle &yogaStyle);
ViewProps(const ViewProps &sourceProps, const RawProps &rawProps);

View File

@ -10,9 +10,7 @@
namespace facebook {
namespace react {
ComponentName ViewShadowNode::getComponentName() const {
return ComponentName("View");
}
const char ViewComponentName[] = "View";
} // namespace react
} // namespace facebook

View File

@ -7,27 +7,20 @@
#pragma once
#include <memory>
#include <fabric/components/view/ViewProps.h>
#include <fabric/components/view/ConcreteViewShadowNode.h>
namespace facebook {
namespace react {
class ViewShadowNode;
extern const char ViewComponentName[];
using SharedViewShadowNode = std::shared_ptr<const ViewShadowNode>;
class ViewShadowNode final:
public ConcreteViewShadowNode<ViewProps, ViewEventEmitter> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
ComponentName getComponentName() const override;
};
using ViewShadowNode =
ConcreteViewShadowNode<
ViewComponentName,
ViewProps,
ViewEventEmitter
>;
} // namespace react
} // namespace facebook

View File

@ -37,22 +37,11 @@ public:
eventDispatcher_(eventDispatcher) {}
ComponentHandle getComponentHandle() const override {
return typeid(ShadowNodeT).hash_code();
return ShadowNodeT::Handle();
}
ComponentName getComponentName() const override {
// Even if this looks suboptimal, it is the only way to call
// a virtual non-static method of `ShadowNodeT`.
// Because it is not a hot path (it is executed once per an app run),
// it's fine.
return std::make_shared<ShadowNodeT>(
0,
0,
std::make_shared<const ConcreteProps>(),
nullptr,
ShadowNode::emptySharedShadowNodeSharedList(),
nullptr
)->ShadowNodeT::getComponentName();
return ShadowNodeT::Name();
}
SharedShadowNode createShadowNode(
@ -74,6 +63,7 @@ public:
);
adopt(shadowNode);
return shadowNode;
}
@ -123,6 +113,7 @@ protected:
virtual void adopt(UnsharedShadowNode shadowNode) const {
// Default implementation does nothing.
assert(shadowNode->getComponentHandle() == getComponentHandle());
}
private:

View File

@ -35,7 +35,7 @@ using SharedRawProps = std::shared_ptr<const RawProps>;
* Practically, it's something that concrete ShadowNode and concrete
* ComponentDescriptor have in common.
*/
using ComponentHandle = size_t;
using ComponentHandle = int64_t;
/*
* String identifier for components used for addressing them from

View File

@ -19,7 +19,11 @@ namespace react {
* `ConcreteShadowNode` is a default implementation of `ShadowNode` interface
* with many handy features.
*/
template <typename PropsT, typename EventEmitterT = EventEmitter>
template <
const char *concreteComponentName,
typename PropsT,
typename EventEmitterT = EventEmitter
>
class ConcreteShadowNode: public ShadowNode {
static_assert(std::is_base_of<Props, PropsT>::value, "PropsT must be a descendant of Props");
@ -30,6 +34,14 @@ public:
using SharedConcreteEventEmitter = std::shared_ptr<const EventEmitterT>;
using SharedConcreteShadowNode = std::shared_ptr<const ConcreteShadowNode>;
static ComponentName Name() {
return ComponentName(concreteComponentName);
}
static ComponentHandle Handle() {
return ComponentHandle(concreteComponentName);
}
static SharedConcreteProps Props(const RawProps &rawProps, const SharedProps &baseProps = nullptr) {
return std::make_shared<const PropsT>(baseProps ? *std::static_pointer_cast<const PropsT>(baseProps) : PropsT(), rawProps);
}
@ -69,8 +81,12 @@ public:
children
) {}
virtual ComponentHandle getComponentHandle() const {
return typeid(*this).hash_code();
ComponentName getComponentName() const override {
return ComponentName(concreteComponentName);
}
ComponentHandle getComponentHandle() const override {
return reinterpret_cast<ComponentHandle>(concreteComponentName);
}
const SharedConcreteProps getProps() const {

View File

@ -14,7 +14,8 @@ using namespace facebook::react;
TEST(ComponentDescriptorTest, createShadowNode) {
SharedComponentDescriptor descriptor = std::make_shared<TestComponentDescriptor>(nullptr);
ASSERT_EQ(descriptor->getComponentHandle(), typeid(TestShadowNode).hash_code());
ASSERT_EQ(descriptor->getComponentHandle(), TestShadowNode::Handle());
ASSERT_STREQ(descriptor->getComponentName().c_str(), TestShadowNode::Name().c_str());
ASSERT_STREQ(descriptor->getComponentName().c_str(), "Test");
RawProps raw;
@ -22,7 +23,8 @@ TEST(ComponentDescriptorTest, createShadowNode) {
SharedProps props = descriptor->cloneProps(nullptr, raw);
SharedShadowNode node = descriptor->createShadowNode(9, 1, descriptor->createEventEmitter(0, 9), props);
ASSERT_EQ(node->getComponentHandle(), typeid(TestShadowNode).hash_code());
ASSERT_EQ(node->getComponentHandle(), TestShadowNode::Handle());
ASSERT_STREQ(node->getComponentName().c_str(), TestShadowNode::Name().c_str());
ASSERT_STREQ(node->getComponentName().c_str(), "Test");
ASSERT_EQ(node->getTag(), 9);
ASSERT_EQ(node->getRootTag(), 1);
@ -38,7 +40,6 @@ TEST(ComponentDescriptorTest, cloneShadowNode) {
SharedShadowNode node = descriptor->createShadowNode(9, 1, descriptor->createEventEmitter(0, 9), props);
SharedShadowNode cloned = descriptor->cloneShadowNode(node);
ASSERT_EQ(cloned->getComponentHandle(), typeid(TestShadowNode).hash_code());
ASSERT_STREQ(cloned->getComponentName().c_str(), "Test");
ASSERT_EQ(cloned->getTag(), 9);
ASSERT_EQ(cloned->getRootTag(), 1);

View File

@ -35,8 +35,6 @@ TEST(ShadowNodeTest, handleShadowNodeCreation) {
ASSERT_EQ(node->getTag(), 9);
ASSERT_EQ(node->getRootTag(), 1);
ASSERT_EQ(node->getEventEmitter(), nullptr);
TestShadowNode *nodePtr = node.get();
ASSERT_EQ(node->getComponentHandle(), typeid(*nodePtr).hash_code());
ASSERT_EQ(node->getChildren()->size(), 0);
ASSERT_STREQ(node->getProps()->nativeId.c_str(), "testNativeID");

View File

@ -36,6 +36,8 @@ private:
int number_ {0};
};
static const char TestComponentName[] = "Test";
class TestProps : public Props {
public:
using Props::Props;
@ -46,21 +48,12 @@ using SharedTestProps = std::shared_ptr<const TestProps>;
class TestShadowNode;
using SharedTestShadowNode = std::shared_ptr<const TestShadowNode>;
class TestShadowNode : public ConcreteShadowNode<TestProps> {
class TestShadowNode : public ConcreteShadowNode<TestComponentName, TestProps> {
public:
using ConcreteShadowNode::ConcreteShadowNode;
ComponentName getComponentName() const override {
return ComponentName("Test");
}
};
class TestComponentDescriptor: public ConcreteComponentDescriptor<TestShadowNode> {
public:
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
// TODO (shergin): Why does this gets repeated here and the shadow node class?
ComponentName getComponentName() const override {
return "Test";
}
};