[nativert] Add utility function to convert strings into numbers. (#151467)

Summary:

nativert RFC: https://github.com/zhxchen17/rfcs/blob/master/RFC-0043-torch-native-runtime.md

To land the runtime into PyTorch core, we will gradually land logical parts of the code into the Github issue and get each piece properly reviewed.

This diff adds a small library to convert strings into numbers which will later be used for parsing graph IR.

Differential Revision: D73133034

## Test Plan

c10 unittests

Pull Request resolved: https://github.com/pytorch/pytorch/pull/151467
Approved by: https://github.com/cyyever, https://github.com/albanD
This commit is contained in:
Zhengxu Chen
2025-04-30 21:20:52 +00:00
committed by PyTorch MergeBot
parent 22ecaeb145
commit 5a66c1d921
3 changed files with 135 additions and 0 deletions

View File

@@ -77,4 +77,60 @@ TEST(StringUtilTest, testStrMulti) {
}
} // namespace test_str_multi
namespace test_try_to {
TEST(tryToTest, Int64T) {
const std::vector<std::pair<const char*, int64_t>> valid_examples = {
{"123", 123},
{"+456", 456},
{"-123", -123},
{"0x123", 291},
{"00123", 83},
{"000", 0},
};
for (const auto& [str, num] : valid_examples) {
EXPECT_EQ(c10::tryToNumber<int64_t>(str), num);
EXPECT_EQ(c10::tryToNumber<int64_t>(std::string{str}), num);
}
const std::vector<const char*> invalid_examples = {
"123abc",
"123.45",
"",
"12345678901234567890", // overflow
};
for (const auto str : invalid_examples) {
EXPECT_FALSE(c10::tryToNumber<int64_t>(str).has_value());
EXPECT_FALSE(c10::tryToNumber<int64_t>(std::string{str}).has_value());
}
EXPECT_FALSE(c10::tryToNumber<int64_t>(nullptr).has_value());
}
TEST(tryToTest, Double) {
const std::vector<std::pair<const char*, double>> valid_examples = {
{"123.45", 123.45},
{"-123.45", -123.45},
{"123", 123.},
{".5", 0.5},
{"-.02", -0.02},
{"5e-2", 5e-2},
{"1e+3", 1e3},
{"0x123.45", 291.26953125},
};
for (const auto& [str, num] : valid_examples) {
EXPECT_EQ(c10::tryToNumber<double>(str), num);
EXPECT_EQ(c10::tryToNumber<double>(std::string{str}), num);
}
const std::vector<const char*> invalid_examples = {
"123abc",
"",
"1e309", // overflow
};
for (const auto str : invalid_examples) {
EXPECT_FALSE(c10::tryToNumber<double>(str).has_value());
EXPECT_FALSE(c10::tryToNumber<double>(std::string{str}).has_value());
}
EXPECT_FALSE(c10::tryToNumber<double>(nullptr).has_value());
}
} // namespace test_try_to
} // namespace

View File

@@ -146,4 +146,58 @@ size_t ReplaceAll(std::string& s, std::string_view from, std::string_view to) {
return numReplaced;
}
template <>
std::optional<int64_t> tryToNumber<int64_t>(const std::string& symbol) {
return tryToNumber<int64_t>(symbol.c_str());
}
template <>
std::optional<int64_t> tryToNumber<int64_t>(const char* symbol) {
// TODO Using strtoll for portability. Consider using std::from_chars in the
// future. According to https://libcxx.llvm.org/Status/Cxx17.html,
// std::from_chars is not supported until clang 20. We will need MSVC to also
// fully support std::from_chars.
if (!symbol) {
return std::nullopt;
}
char* end = nullptr;
errno = 0;
int64_t value = strtoll(symbol, &end, 0);
if (errno != 0) {
errno = 0;
return std::nullopt;
}
if (*end != '\0' || end == symbol) {
return std::nullopt;
}
return value;
}
template <>
std::optional<double> tryToNumber<double>(const std::string& symbol) {
return tryToNumber<double>(symbol.c_str());
}
template <>
std::optional<double> tryToNumber<double>(const char* symbol) {
// TODO Using strtod for portability. Consider using std::from_chars in the
// future. According to https://libcxx.llvm.org/Status/Cxx17.html,
// std::from_chars is not supported until clang 20. We will need MSVC to also
// fully support std::from_chars.
if (!symbol) {
return std::nullopt;
}
char* end = nullptr;
errno = 0;
double value = strtod(symbol, &end);
if (errno != 0) {
errno = 0;
return std::nullopt;
}
if (*end != '\0' || end == symbol) {
return std::nullopt;
}
return value;
}
} // namespace c10

View File

@@ -213,6 +213,31 @@ inline void printQuotedString(std::ostream& stmt, const std::string_view str) {
stmt << "\"";
}
template <typename T>
std::optional<T> tryToNumber(const char* symbol) = delete;
template <typename T>
std::optional<T> tryToNumber(const std::string& symbol) = delete;
/*
* Convert a string to a 64 bit integer. Trailing whitespaces are not supported.
* Similarly, integer string with trailing characters like "123abc" will be
* rejected.
*/
template <>
C10_API std::optional<int64_t> tryToNumber<int64_t>(const char* symbol);
template <>
C10_API std::optional<int64_t> tryToNumber<int64_t>(const std::string& symbol);
/*
* Convert a string to a double. Trailing whitespaces are not supported.
* Similarly, integer string with trailing characters like "123abc" will
* be rejected.
*/
template <>
C10_API std::optional<double> tryToNumber<double>(const char* symbol);
template <>
C10_API std::optional<double> tryToNumber<double>(const std::string& symbol);
} // namespace c10
C10_CLANG_DIAGNOSTIC_POP()