C++ Cleanup 9/N: YGAssert (#1353)

Summary:
X-link: https://github.com/facebook/react-native/pull/39201

Pull Request resolved: https://github.com/facebook/yoga/pull/1353

## This diff

This moves and renames `YGAssert`, and removes it from the public API, since external users should not need to call into internal Yoga assert functions, and the current API prevents us from making this a macro later to include the condition in the message.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Reviewed By: rshest

Differential Revision: D48769809

fbshipit-source-id: b5480ac54781bc01b00c158b07d2d751fac87d37
This commit is contained in:
Nick Gerleman
2023-09-04 11:20:17 -07:00
committed by Facebook GitHub Bot
parent 7be985d97c
commit 7d8b9176fd
6 changed files with 118 additions and 96 deletions

View File

@@ -11,7 +11,6 @@
#include <cmath>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <yoga/Yoga.h>
#include <yoga/Yoga-internal.h>
@@ -19,6 +18,7 @@
#include <yoga/algorithm/CollectFlexItemsRowValues.h>
#include <yoga/algorithm/FlexDirection.h>
#include <yoga/algorithm/ResolveValue.h>
#include <yoga/debug/AssertFatal.h>
#include <yoga/debug/Log.h>
#include <yoga/debug/NodeToString.h>
#include <yoga/event/event.h>
@@ -190,8 +190,9 @@ int32_t gConfigInstanceCount = 0;
YOGA_EXPORT WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) {
auto* node = new yoga::Node{static_cast<yoga::Config*>(config)};
YGAssert(config != nullptr, "Tried to construct YGNode with null config");
YGAssertWithConfig(
yoga::assertFatal(
config != nullptr, "Tried to construct YGNode with null config");
yoga::assertFatalWithConfig(
config, node != nullptr, "Could not allocate memory for node");
Event::publish<Event::NodeAllocation>(node, {config});
@@ -210,7 +211,7 @@ YOGA_EXPORT YGNodeRef YGNodeNew(void) {
YOGA_EXPORT YGNodeRef YGNodeClone(YGNodeRef oldNodeRef) {
auto oldNode = static_cast<yoga::Node*>(oldNodeRef);
auto node = new yoga::Node(*oldNode);
YGAssertWithConfig(
yoga::assertFatalWithConfig(
oldNode->getConfig(),
node != nullptr,
"Could not allocate memory for node");
@@ -312,12 +313,12 @@ YOGA_EXPORT void YGNodeInsertChild(
auto owner = static_cast<yoga::Node*>(ownerRef);
auto child = static_cast<yoga::Node*>(childRef);
YGAssertWithNode(
yoga::assertFatalWithNode(
owner,
child->getOwner() == nullptr,
"Child already has a owner, it must be removed first.");
YGAssertWithNode(
yoga::assertFatalWithNode(
owner,
!owner->hasMeasureFunc(),
"Cannot add child: Nodes with measure functions cannot have children.");
@@ -456,7 +457,7 @@ YOGA_EXPORT YGNodeRef YGNodeGetParent(const YGNodeRef node) {
YOGA_EXPORT void YGNodeMarkDirty(const YGNodeRef nodeRef) {
auto node = static_cast<yoga::Node*>(nodeRef);
YGAssertWithNode(
yoga::assertFatalWithNode(
node,
node->hasMeasureFunc(),
"Only leaf nodes with custom measure functions "
@@ -943,7 +944,7 @@ YOGA_EXPORT YGValue YGNodeStyleGetMaxHeight(const YGNodeConstRef node) {
YOGA_EXPORT type YGNodeLayoutGet##name( \
const YGNodeRef nodeRef, const YGEdge edge) { \
auto node = static_cast<yoga::Node*>(nodeRef); \
YGAssertWithNode( \
yoga::assertFatalWithNode( \
node, \
edge <= YGEdgeEnd, \
"Cannot get layout properties of multi-edge shorthands"); \
@@ -1053,7 +1054,7 @@ static float YGBaseline(yoga::Node* node, void* layoutContext) {
Event::publish<Event::NodeBaselineEnd>(node);
YGAssertWithNode(
yoga::assertFatalWithNode(
node,
!yoga::isUndefined(baseline),
"Expect custom baseline function to not return NaN");
@@ -1669,7 +1670,7 @@ static void YGNodeWithMeasureFuncSetMeasuredDimensions(
LayoutData& layoutMarkerData,
void* const layoutContext,
const LayoutPassReason reason) {
YGAssertWithNode(
yoga::assertFatalWithNode(
node,
node->hasMeasureFunc(),
"Expected node to have custom measure function");
@@ -2731,14 +2732,14 @@ static void YGNodelayoutImpl(
const uint32_t depth,
const uint32_t generationCount,
const LayoutPassReason reason) {
YGAssertWithNode(
yoga::assertFatalWithNode(
node,
yoga::isUndefined(availableWidth)
? widthMeasureMode == YGMeasureModeUndefined
: true,
"availableWidth is indefinite so widthMeasureMode must be "
"YGMeasureModeUndefined");
YGAssertWithNode(
yoga::assertFatalWithNode(
node,
yoga::isUndefined(availableHeight)
? heightMeasureMode == YGMeasureModeUndefined
@@ -4089,7 +4090,7 @@ bool YGLayoutNodeInternal(
YOGA_EXPORT void YGConfigSetPointScaleFactor(
const YGConfigRef config,
const float pixelsInPoint) {
YGAssertWithConfig(
yoga::assertFatalWithConfig(
config,
pixelsInPoint >= 0.0f,
"Scale factor should not be less than zero");
@@ -4287,56 +4288,6 @@ YOGA_EXPORT void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) {
}
}
static void fatalWithMessage(const char* message) {
#if defined(__cpp_exceptions)
throw std::logic_error(message);
#else
std::terminate();
#endif
}
void YGAssert(const bool condition, const char* message) {
if (!condition) {
yoga::log(
static_cast<yoga::Node*>(nullptr),
YGLogLevelFatal,
nullptr,
"%s\n",
message);
fatalWithMessage(message);
}
}
void YGAssertWithNode(
const YGNodeRef node,
const bool condition,
const char* message) {
if (!condition) {
yoga::log(
static_cast<yoga::Node*>(node),
YGLogLevelFatal,
nullptr,
"%s\n",
message);
fatalWithMessage(message);
}
}
void YGAssertWithConfig(
const YGConfigRef config,
const bool condition,
const char* message) {
if (!condition) {
yoga::log(
static_cast<yoga::Config*>(config),
YGLogLevelFatal,
nullptr,
"%s\n",
message);
fatalWithMessage(message);
}
}
YOGA_EXPORT void YGConfigSetExperimentalFeatureEnabled(
const YGConfigRef config,
const YGExperimentalFeature feature,

View File

@@ -7,16 +7,9 @@
#pragma once
#include <assert.h>
#include <math.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <stdint.h>
#include <yoga/YGEnums.h>
#include <yoga/YGMacros.h>
@@ -303,15 +296,6 @@ WIN_EXPORT float YGNodeLayoutGetBorder(YGNodeRef node, YGEdge edge);
WIN_EXPORT float YGNodeLayoutGetPadding(YGNodeRef node, YGEdge edge);
WIN_EXPORT void YGConfigSetLogger(YGConfigRef config, YGLogger logger);
WIN_EXPORT void YGAssert(bool condition, const char* message);
WIN_EXPORT void YGAssertWithNode(
YGNodeRef node,
bool condition,
const char* message);
WIN_EXPORT void YGAssertWithConfig(
YGConfigRef config,
bool condition,
const char* message);
// Set this to number of pixels in 1 point to round calculation results If you
// want to avoid rounding - set PointScaleFactor to 0
WIN_EXPORT void YGConfigSetPointScaleFactor(

View File

@@ -9,6 +9,8 @@
#include <yoga/Yoga.h>
#include <yoga/debug/AssertFatal.h>
namespace facebook::yoga {
inline bool isRow(const YGFlexDirection flexDirection) {
@@ -55,11 +57,7 @@ inline YGEdge leadingEdge(const YGFlexDirection flexDirection) {
return YGEdgeRight;
}
YGAssert(false, "Invalid YGFlexDirection");
// Avoid "not all control paths return a value" warning until next diff adds
// assert with [[noreturn]]
return YGEdgeTop;
fatalWithMessage("Invalid YGFlexDirection");
}
inline YGEdge trailingEdge(const YGFlexDirection flexDirection) {
@@ -74,11 +72,7 @@ inline YGEdge trailingEdge(const YGFlexDirection flexDirection) {
return YGEdgeLeft;
}
YGAssert(false, "Invalid YGFlexDirection");
// Avoid "not all control paths return a value" warning until next diff adds
// assert with [[noreturn]]
return YGEdgeTop;
fatalWithMessage("Invalid YGFlexDirection");
}
} // namespace facebook::yoga

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <stdexcept>
#include <yoga/debug/AssertFatal.h>
#include <yoga/debug/Log.h>
namespace facebook::yoga {
[[noreturn]] void fatalWithMessage(const char* message) {
#if defined(__cpp_exceptions)
throw std::logic_error(message);
#else
std::terminate();
#endif
}
void assertFatal(const bool condition, const char* message) {
if (!condition) {
yoga::log(
static_cast<yoga::Node*>(nullptr),
YGLogLevelFatal,
nullptr,
"%s\n",
message);
fatalWithMessage(message);
}
}
void assertFatalWithNode(
const YGNodeRef node,
const bool condition,
const char* message) {
if (!condition) {
yoga::log(
static_cast<yoga::Node*>(node),
YGLogLevelFatal,
nullptr,
"%s\n",
message);
fatalWithMessage(message);
}
}
void assertFatalWithConfig(
const YGConfigRef config,
const bool condition,
const char* message) {
if (!condition) {
yoga::log(
static_cast<yoga::Config*>(config),
YGLogLevelFatal,
nullptr,
"%s\n",
message);
fatalWithMessage(message);
}
}
} // namespace facebook::yoga

25
yoga/debug/AssertFatal.h Normal file
View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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 <yoga/Yoga.h>
#include <yoga/node/Node.h>
#include <yoga/config/Config.h>
namespace facebook::yoga {
[[noreturn]] void fatalWithMessage(const char* message);
void assertFatal(bool condition, const char* message);
void assertFatalWithNode(YGNodeRef node, bool condition, const char* message);
void assertFatalWithConfig(
YGConfigRef config,
bool condition,
const char* message);
} // namespace facebook::yoga

View File

@@ -10,13 +10,15 @@
#include <yoga/algorithm/FlexDirection.h>
#include <yoga/algorithm/ResolveValue.h>
#include <yoga/debug/AssertFatal.h>
#include <yoga/node/Node.h>
#include <yoga/numeric/Comparison.h>
namespace facebook::yoga {
Node::Node(yoga::Config* config) : config_{config} {
YGAssert(config != nullptr, "Attempting to construct Node with null config");
yoga::assertFatal(
config != nullptr, "Attempting to construct Node with null config");
flags_.hasNewLayout = true;
if (config->useWebDefaults()) {
@@ -234,7 +236,7 @@ void Node::setMeasureFunc(decltype(Node::measure_) measureFunc) {
// places in Litho
setNodeType(YGNodeTypeDefault);
} else {
YGAssertWithNode(
yoga::assertFatalWithNode(
this,
children_.size() == 0,
"Cannot set measure function: Nodes with measure functions cannot have "
@@ -274,8 +276,9 @@ void Node::insertChild(Node* child, uint32_t index) {
}
void Node::setConfig(yoga::Config* config) {
YGAssert(config != nullptr, "Attempting to set a null config on a Node");
YGAssertWithConfig(
yoga::assertFatal(
config != nullptr, "Attempting to set a null config on a Node");
yoga::assertFatalWithConfig(
config,
config->useWebDefaults() == config_->useWebDefaults(),
"UseWebDefaults may not be changed after constructing a Node");
@@ -592,11 +595,11 @@ FloatOptional Node::getTrailingPaddingAndBorder(
}
void Node::reset() {
YGAssertWithNode(
yoga::assertFatalWithNode(
this,
children_.size() == 0,
"Cannot reset a node which still has children attached");
YGAssertWithNode(
yoga::assertFatalWithNode(
this, owner_ == nullptr, "Cannot reset a node still attached to a owner");
*this = Node{getConfig()};