C++ Cleanup 4/N: Reorganize Log and YGNodePrint (#1354)

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

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

## This diff

Moves these files to a `yoga/debug` subdirectory and does some mild renaming, namespace adjustment, and removes Yoga internal log function from list of library exports.

## 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: D48763820

fbshipit-source-id: 7e3ed7354497a7e85b2a1f20ab50b0e2baeb4862
This commit is contained in:
Nick Gerleman
2023-08-29 21:32:56 -07:00
committed by Facebook GitHub Bot
parent 59f75b0194
commit 9ba8aa409c
6 changed files with 71 additions and 78 deletions

58
yoga/debug/Log.cpp Normal file
View File

@@ -0,0 +1,58 @@
/*
* 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 <yoga/debug/Log.h>
namespace facebook::yoga {
namespace {
void vlog(
yoga::Config* config,
yoga::Node* node,
YGLogLevel level,
void* context,
const char* format,
va_list args) {
yoga::Config* logConfig = config != nullptr
? config
: static_cast<yoga::Config*>(YGConfigGetDefault());
logConfig->log(node, level, context, format, args);
}
} // namespace
void log(
yoga::Node* node,
YGLogLevel level,
void* context,
const char* format,
...) noexcept {
va_list args;
va_start(args, format);
vlog(
node == nullptr ? nullptr : node->getConfig(),
node,
level,
context,
format,
args);
va_end(args);
}
void log(
yoga::Config* config,
YGLogLevel level,
void* context,
const char* format,
...) noexcept {
va_list args;
va_start(args, format);
vlog(config, nullptr, level, context, format, args);
va_end(args);
}
} // namespace facebook::yoga

30
yoga/debug/Log.h Normal file
View File

@@ -0,0 +1,30 @@
/*
* 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/YGEnums.h>
#include <yoga/node/Node.h>
#include <yoga/config/Config.h>
namespace facebook::yoga {
void log(
yoga::Node* node,
YGLogLevel level,
void*,
const char* message,
...) noexcept;
void log(
yoga::Config* config,
YGLogLevel level,
void*,
const char* format,
...) noexcept;
} // namespace facebook::yoga

243
yoga/debug/NodeToString.cpp Normal file
View File

@@ -0,0 +1,243 @@
/*
* 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.
*/
#ifdef DEBUG
#include <stdarg.h>
#include <yoga/YGEnums.h>
#include <yoga/debug/NodeToString.h>
#include <yoga/Yoga-internal.h>
#include <yoga/Utils.h>
namespace facebook::yoga {
static void indent(std::string& base, uint32_t level) {
for (uint32_t i = 0; i < level; ++i) {
base.append(" ");
}
}
static bool areFourValuesEqual(const Style::Edges& four) {
return YGValueEqual(four[0], four[1]) && YGValueEqual(four[0], four[2]) &&
YGValueEqual(four[0], four[3]);
}
static void appendFormattedString(std::string& str, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
va_list argsCopy;
va_copy(argsCopy, args);
std::vector<char> buf(1 + vsnprintf(NULL, 0, fmt, args));
va_end(args);
vsnprintf(buf.data(), buf.size(), fmt, argsCopy);
va_end(argsCopy);
std::string result = std::string(buf.begin(), buf.end() - 1);
str.append(result);
}
static void appendFloatOptionalIfDefined(
std::string& base,
const std::string key,
const YGFloatOptional num) {
if (!num.isUndefined()) {
appendFormattedString(base, "%s: %g; ", key.c_str(), num.unwrap());
}
}
static void appendNumberIfNotUndefined(
std::string& base,
const std::string key,
const YGValue number) {
if (number.unit != YGUnitUndefined) {
if (number.unit == YGUnitAuto) {
base.append(key + ": auto; ");
} else {
std::string unit = number.unit == YGUnitPoint ? "px" : "%%";
appendFormattedString(
base, "%s: %g%s; ", key.c_str(), number.value, unit.c_str());
}
}
}
static void appendNumberIfNotAuto(
std::string& base,
const std::string& key,
const YGValue number) {
if (number.unit != YGUnitAuto) {
appendNumberIfNotUndefined(base, key, number);
}
}
static void appendNumberIfNotZero(
std::string& base,
const std::string& str,
const YGValue number) {
if (number.unit == YGUnitAuto) {
base.append(str + ": auto; ");
} else if (!YGFloatsEqual(number.value, 0)) {
appendNumberIfNotUndefined(base, str, number);
}
}
static void appendEdges(
std::string& base,
const std::string& key,
const Style::Edges& edges) {
if (areFourValuesEqual(edges)) {
auto edgeValue = yoga::Node::computeEdgeValueForColumn(
edges, YGEdgeLeft, CompactValue::ofZero());
appendNumberIfNotZero(base, key, edgeValue);
} else {
for (int edge = YGEdgeLeft; edge != YGEdgeAll; ++edge) {
std::string str = key + "-" + YGEdgeToString(static_cast<YGEdge>(edge));
appendNumberIfNotZero(base, str, edges[edge]);
}
}
}
static void appendEdgeIfNotUndefined(
std::string& base,
const std::string& str,
const Style::Edges& edges,
const YGEdge edge) {
// TODO: this doesn't take RTL / YGEdgeStart / YGEdgeEnd into account
auto value = (edge == YGEdgeLeft || edge == YGEdgeRight)
? yoga::Node::computeEdgeValueForRow(
edges, edge, edge, CompactValue::ofUndefined())
: yoga::Node::computeEdgeValueForColumn(
edges, edge, CompactValue::ofUndefined());
appendNumberIfNotUndefined(base, str, value);
}
void nodeToString(
std::string& str,
yoga::Node* node,
YGPrintOptions options,
uint32_t level) {
indent(str, level);
appendFormattedString(str, "<div ");
if (options & YGPrintOptionsLayout) {
appendFormattedString(str, "layout=\"");
appendFormattedString(
str, "width: %g; ", node->getLayout().dimensions[YGDimensionWidth]);
appendFormattedString(
str, "height: %g; ", node->getLayout().dimensions[YGDimensionHeight]);
appendFormattedString(
str, "top: %g; ", node->getLayout().position[YGEdgeTop]);
appendFormattedString(
str, "left: %g;", node->getLayout().position[YGEdgeLeft]);
appendFormattedString(str, "\" ");
}
if (options & YGPrintOptionsStyle) {
appendFormattedString(str, "style=\"");
const auto& style = node->getStyle();
if (style.flexDirection() != yoga::Node{}.getStyle().flexDirection()) {
appendFormattedString(
str,
"flex-direction: %s; ",
YGFlexDirectionToString(style.flexDirection()));
}
if (style.justifyContent() != yoga::Node{}.getStyle().justifyContent()) {
appendFormattedString(
str,
"justify-content: %s; ",
YGJustifyToString(style.justifyContent()));
}
if (style.alignItems() != yoga::Node{}.getStyle().alignItems()) {
appendFormattedString(
str, "align-items: %s; ", YGAlignToString(style.alignItems()));
}
if (style.alignContent() != yoga::Node{}.getStyle().alignContent()) {
appendFormattedString(
str, "align-content: %s; ", YGAlignToString(style.alignContent()));
}
if (style.alignSelf() != yoga::Node{}.getStyle().alignSelf()) {
appendFormattedString(
str, "align-self: %s; ", YGAlignToString(style.alignSelf()));
}
appendFloatOptionalIfDefined(str, "flex-grow", style.flexGrow());
appendFloatOptionalIfDefined(str, "flex-shrink", style.flexShrink());
appendNumberIfNotAuto(str, "flex-basis", style.flexBasis());
appendFloatOptionalIfDefined(str, "flex", style.flex());
if (style.flexWrap() != yoga::Node{}.getStyle().flexWrap()) {
appendFormattedString(
str, "flex-wrap: %s; ", YGWrapToString(style.flexWrap()));
}
if (style.overflow() != yoga::Node{}.getStyle().overflow()) {
appendFormattedString(
str, "overflow: %s; ", YGOverflowToString(style.overflow()));
}
if (style.display() != yoga::Node{}.getStyle().display()) {
appendFormattedString(
str, "display: %s; ", YGDisplayToString(style.display()));
}
appendEdges(str, "margin", style.margin());
appendEdges(str, "padding", style.padding());
appendEdges(str, "border", style.border());
if (yoga::Node::computeColumnGap(
style.gap(), CompactValue::ofUndefined()) !=
yoga::Node::computeColumnGap(
yoga::Node{}.getStyle().gap(), CompactValue::ofUndefined())) {
appendNumberIfNotUndefined(
str, "column-gap", style.gap()[YGGutterColumn]);
}
if (yoga::Node::computeRowGap(style.gap(), CompactValue::ofUndefined()) !=
yoga::Node::computeRowGap(
yoga::Node{}.getStyle().gap(), CompactValue::ofUndefined())) {
appendNumberIfNotUndefined(str, "row-gap", style.gap()[YGGutterRow]);
}
appendNumberIfNotAuto(str, "width", style.dimensions()[YGDimensionWidth]);
appendNumberIfNotAuto(str, "height", style.dimensions()[YGDimensionHeight]);
appendNumberIfNotAuto(
str, "max-width", style.maxDimensions()[YGDimensionWidth]);
appendNumberIfNotAuto(
str, "max-height", style.maxDimensions()[YGDimensionHeight]);
appendNumberIfNotAuto(
str, "min-width", style.minDimensions()[YGDimensionWidth]);
appendNumberIfNotAuto(
str, "min-height", style.minDimensions()[YGDimensionHeight]);
if (style.positionType() != yoga::Node{}.getStyle().positionType()) {
appendFormattedString(
str, "position: %s; ", YGPositionTypeToString(style.positionType()));
}
appendEdgeIfNotUndefined(str, "left", style.position(), YGEdgeLeft);
appendEdgeIfNotUndefined(str, "right", style.position(), YGEdgeRight);
appendEdgeIfNotUndefined(str, "top", style.position(), YGEdgeTop);
appendEdgeIfNotUndefined(str, "bottom", style.position(), YGEdgeBottom);
appendFormattedString(str, "\" ");
if (node->hasMeasureFunc()) {
appendFormattedString(str, "has-custom-measure=\"true\"");
}
}
appendFormattedString(str, ">");
const uint32_t childCount = static_cast<uint32_t>(node->getChildren().size());
if (options & YGPrintOptionsChildren && childCount > 0) {
for (uint32_t i = 0; i < childCount; i++) {
appendFormattedString(str, "\n");
nodeToString(str, node->getChild(i), options, level + 1);
}
appendFormattedString(str, "\n");
indent(str, level);
}
appendFormattedString(str, "</div>");
}
} // namespace facebook::yoga
#endif

27
yoga/debug/NodeToString.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* 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.
*/
#ifdef DEBUG
#pragma once
#include <string>
#include <yoga/Yoga.h>
#include <yoga/node/Node.h>
namespace facebook::yoga {
void nodeToString(
std::string& str,
yoga::Node* node,
YGPrintOptions options,
uint32_t level);
} // namespace facebook::yoga
#endif