mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-01-12 22:50:10 +08:00
Fabric: *Informal* DebugStringConvertible interface
Summary: Informal `DebugStringConvertible` interface serves the same purpose as `DebugStringConvertible` abstract class (providing universal pretty-printing interface) but relies on C++ static overloading instead of virtual dispatch. This approach has some advantages and disadvantages: Pros: * It's more clear and scoped. It's simpler to implement debug-printing functionality aside of normal production code. * It's more composable. * It allows print types that are not classes. * For some classes using `DebugStringConvertible` means that we have to use virtual inheritance (which might be undesirable and affect *production* performance (if a compiler isn't smart enough to remove empty base class). * For some highly lean classes (that designed to be as small as possible) adding base (even empty-in-prod) classes kinda... smells. Cons: The particular implementations cannot rely on dynamic dispatch which can complicate printing classes with meaningful differences between sampling classes (e.g. ShadowNode subclasses). That's why we don't remove `DebugStringConvertible` class yet. Reviewed By: mdvacca Differential Revision: D14715081 fbshipit-source-id: 1d397dbf81dc6d1dff0cc3f64ad09f10afe0085d
This commit is contained in:
committed by
Facebook Github Bot
parent
cabc9d1ab3
commit
0fb27a7633
@@ -78,6 +78,19 @@ std::string DebugStringConvertible::getDebugDescription(
|
||||
DebugStringConvertibleOptions options) const {
|
||||
auto nameString = getDebugName();
|
||||
auto valueString = getDebugValue();
|
||||
|
||||
// Convention:
|
||||
// If `name` and `value` are empty, `description` is also empty.
|
||||
if (nameString.empty() && valueString.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Convention:
|
||||
// If `name` is empty and `value` isn't empty, `description` equals `value`.
|
||||
if (nameString.empty()) {
|
||||
return valueString;
|
||||
}
|
||||
|
||||
auto childrenString = getDebugChildrenDescription(options);
|
||||
auto propsString = getDebugPropsDescription(options);
|
||||
|
||||
|
||||
@@ -34,10 +34,16 @@ struct DebugStringConvertibleOptions {
|
||||
int maximumDepth{INT_MAX};
|
||||
};
|
||||
|
||||
// Abstract class describes conformance to DebugStringConvertible concept
|
||||
// and implements basic recursive debug string assembly algorithm.
|
||||
// Use this as a base class for providing a debugging textual representation
|
||||
// of your class.
|
||||
/*
|
||||
* Abstract class describes conformance to DebugStringConvertible concept
|
||||
* and implements basic recursive debug string assembly algorithm.
|
||||
* Use this as a base class for providing a debugging textual representation
|
||||
* of your class.
|
||||
*
|
||||
* The `DebugStringConvertible` *class* is obsolete. Whenever possible prefer
|
||||
* implementing standalone functions that conform to the informal
|
||||
* `DebugStringConvertible`-like interface instead of extending this class.
|
||||
*/
|
||||
class DebugStringConvertible {
|
||||
public:
|
||||
virtual ~DebugStringConvertible() = default;
|
||||
@@ -83,7 +89,8 @@ class DebugStringConvertible {};
|
||||
#if RN_DEBUG_STRING_CONVERTIBLE
|
||||
|
||||
/*
|
||||
* Set of particular-format-opinionated functions that convert base types to `std::string`; practically incapsulate `folly:to<>` and `folly::format`.
|
||||
* Set of particular-format-opinionated functions that convert base types to
|
||||
* `std::string`; practically incapsulate `folly:to<>` and `folly::format`.
|
||||
*/
|
||||
std::string toString(std::string const &value);
|
||||
std::string toString(int const &value);
|
||||
@@ -92,6 +99,219 @@ std::string toString(float const &value);
|
||||
std::string toString(double const &value);
|
||||
std::string toString(void const *value);
|
||||
|
||||
/*
|
||||
* *Informal* `DebugStringConvertible` interface.
|
||||
*
|
||||
* The interface consts of several functions which are designed to be composable
|
||||
* and reusable relying on C++ overloading mechanism. Implement appropriate
|
||||
* versions of those functions for your custom type to enable conformance to the
|
||||
* interface:
|
||||
*
|
||||
* - `getDebugName`: Returns a name of the object. Default implementation
|
||||
* returns "Node".
|
||||
*
|
||||
* - `getDebugValue`: Returns a value assosiate with the object. Default
|
||||
* implementation returns an empty string.
|
||||
*
|
||||
* - `getDebugChildren`: Returns a list of `DebugStringConvertible`-compatible
|
||||
* objects which can be considered as *children* of the object. Default
|
||||
* implementation returns an empty list.
|
||||
*
|
||||
* - `getDebugProps`: Returns a list of `DebugStringConvertible` objects which
|
||||
* can be considered as *properties* of the object. Default implementation
|
||||
* returns an empty list.
|
||||
*
|
||||
* - `getDebugDescription`: Returns a string which represents the object in a
|
||||
* human-readable way. Default implementation returns a description of the
|
||||
* subtree rooted at this node, represented in XML-like format using functions
|
||||
* above to form the tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Universal implementation of `getDebugDescription`-family functions for all
|
||||
* types.
|
||||
*/
|
||||
template <typename T>
|
||||
std::string getDebugName(T const &object) {
|
||||
return "Node";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string getDebugValue(T const &object) {
|
||||
return "";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> getDebugChildren(T const &object) {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> getDebugProps(T const &object) {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string getDebugPropsDescription(
|
||||
T const &object,
|
||||
DebugStringConvertibleOptions options) {
|
||||
if (options.depth >= options.maximumDepth) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string propsString = "";
|
||||
|
||||
options.depth++;
|
||||
|
||||
for (auto prop : getDebugProps(object)) {
|
||||
auto name = getDebugName(prop);
|
||||
auto value = getDebugValue(prop);
|
||||
auto children = getDebugPropsDescription(prop, options);
|
||||
auto valueAndChildren =
|
||||
value + (children.empty() ? "" : "(" + children + ")");
|
||||
propsString +=
|
||||
" " + name + (valueAndChildren.empty() ? "" : "=" + valueAndChildren);
|
||||
}
|
||||
|
||||
if (!propsString.empty()) {
|
||||
// Removing leading space character.
|
||||
propsString.erase(propsString.begin());
|
||||
}
|
||||
|
||||
return propsString;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string getDebugChildrenDescription(
|
||||
T const &object,
|
||||
DebugStringConvertibleOptions options) {
|
||||
if (options.depth >= options.maximumDepth) {
|
||||
return "";
|
||||
}
|
||||
|
||||
auto trailing = options.format ? std::string{"\n"} : std::string{""};
|
||||
auto childrenString = std::string{""};
|
||||
options.depth++;
|
||||
|
||||
for (auto child : getDebugChildren(object)) {
|
||||
childrenString += getDebugDescription(child, options) + trailing;
|
||||
}
|
||||
|
||||
if (!childrenString.empty() && !trailing.empty()) {
|
||||
// Removing trailing fragment.
|
||||
childrenString.erase(childrenString.end() - 1);
|
||||
}
|
||||
|
||||
return childrenString;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string getDebugDescription(
|
||||
T const &object,
|
||||
DebugStringConvertibleOptions options = {}) {
|
||||
auto nameString = getDebugName(object);
|
||||
auto valueString = getDebugValue(object);
|
||||
|
||||
// Convention:
|
||||
// If `name` and `value` are empty, `description` is also empty.
|
||||
if (nameString.empty() && valueString.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Convention:
|
||||
// If `name` is empty and `value` isn't empty, `description` equals `value`.
|
||||
if (nameString.empty()) {
|
||||
return valueString;
|
||||
}
|
||||
|
||||
auto childrenString = getDebugChildrenDescription(object, options);
|
||||
auto propsString = getDebugPropsDescription(object, options);
|
||||
|
||||
auto leading =
|
||||
options.format ? std::string(options.depth * 2, ' ') : std::string{""};
|
||||
auto trailing = options.format ? std::string{"\n"} : std::string{""};
|
||||
|
||||
return leading + "<" + nameString +
|
||||
(valueString.empty() ? "" : "=" + valueString) +
|
||||
(propsString.empty() ? "" : " " + propsString) +
|
||||
(childrenString.empty() ? "/>"
|
||||
: ">" + trailing + childrenString + trailing +
|
||||
leading + "</" + nameString + ">");
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions of `getDebugDescription`-family for primitive types.
|
||||
*/
|
||||
// `int`
|
||||
inline std::string getDebugDescription(
|
||||
int number,
|
||||
DebugStringConvertibleOptions options = {}) {
|
||||
return toString(number);
|
||||
}
|
||||
|
||||
// `float`
|
||||
inline std::string getDebugDescription(
|
||||
float number,
|
||||
DebugStringConvertibleOptions options = {}) {
|
||||
return toString(number);
|
||||
}
|
||||
|
||||
// `double`
|
||||
inline std::string getDebugDescription(
|
||||
double number,
|
||||
DebugStringConvertibleOptions options = {}) {
|
||||
return toString(number);
|
||||
}
|
||||
|
||||
// `bool`
|
||||
inline std::string getDebugDescription(
|
||||
bool boolean,
|
||||
DebugStringConvertibleOptions options = {}) {
|
||||
return toString(boolean);
|
||||
}
|
||||
|
||||
// `void *`
|
||||
inline std::string getDebugDescription(
|
||||
void const *pointer,
|
||||
DebugStringConvertibleOptions options = {}) {
|
||||
return toString(pointer);
|
||||
}
|
||||
|
||||
// `std::string`
|
||||
inline std::string getDebugDescription(
|
||||
std::string const &string,
|
||||
DebugStringConvertibleOptions options = {}) {
|
||||
return string;
|
||||
}
|
||||
|
||||
// `std::vector<T>`
|
||||
template <typename T>
|
||||
std::string getDebugName(std::vector<T> const &vector) {
|
||||
return "List";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> getDebugChildren(std::vector<T> const &vector) {
|
||||
return vector;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trivial container for `name` and `value` pair that supports
|
||||
* static `DebugStringConvertible` informal interface.
|
||||
*/
|
||||
struct DebugStringConvertibleObject {
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
inline std::string getDebugName(DebugStringConvertibleObject const &object) {
|
||||
return object.name;
|
||||
}
|
||||
|
||||
inline std::string getDebugValue(DebugStringConvertibleObject const &object) {
|
||||
return object.value;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace react
|
||||
|
||||
Reference in New Issue
Block a user