Skip to content

Commit e96c1ba

Browse files
Refactor Calculator methods to return std::optional.
The methods CalculateString, Tokenize, and CalculateTokens in Calculator are refactored to return std::optional<T> instead of using a boolean return value with an output parameter. This improves clarity by explicitly indicating whether a valid result is available. Call sites in calculator_test.cc and calculator_rewriter.cc are updated to handle the std::optional return type. #codehealth PiperOrigin-RevId: 912442676
1 parent 7684d80 commit e96c1ba

4 files changed

Lines changed: 58 additions & 61 deletions

File tree

src/rewriter/calculator/calculator.cc

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -318,12 +318,11 @@ Calculator::Calculator() {
318318
operator_map_["abs"] = TokenType::FUNC_ABS;
319319
}
320320

321-
bool Calculator::CalculateString(const absl::string_view key,
322-
std::string* result) const {
323-
DCHECK(result);
321+
std::optional<std::string> Calculator::CalculateString(
322+
const absl::string_view key) const {
324323
if (key.empty() || key.size() > kMaxInputLength) {
325324
LOG(ERROR) << "Key is empty or too long.";
326-
return false;
325+
return std::nullopt;
327326
}
328327
std::string normalized_key =
329328
japanese_util::FullWidthAsciiToHalfWidthAscii(key);
@@ -339,37 +338,32 @@ bool Calculator::CalculateString(const absl::string_view key,
339338
absl::string_view(normalized_key.data(), normalized_key.size() - 1);
340339
} else {
341340
// Expression does not start nor end with '='.
342-
result->clear();
343-
return false;
341+
return std::nullopt;
344342
}
345343

346-
TokenSequence tokens;
347-
if (!Tokenize(expression_body, &tokens)) {
344+
std::optional<TokenSequence> tokens = Tokenize(expression_body);
345+
if (!tokens.has_value()) {
348346
// normalized_key is not valid sequence of tokens
349-
result->clear();
350-
return false;
347+
return std::nullopt;
351348
}
352349

353-
double result_value = 0.0;
354-
if (!CalculateTokens(tokens, &result_value)) {
350+
std::optional<double> result_value = CalculateTokens(*tokens);
351+
if (!result_value.has_value()) {
355352
// Calculation is failed. Syntax error or arithmetic error such as
356353
// overflow, divide-by-zero, etc.
357-
result->clear();
358-
return false;
354+
return std::nullopt;
359355
}
360-
*result = absl::StrFormat("%.8g", result_value);
361-
return true;
356+
return absl::StrFormat("%.8g", *result_value);
362357
}
363358

364-
bool Calculator::Tokenize(absl::string_view expression_body,
365-
TokenSequence* tokens) const {
359+
std::optional<Calculator::TokenSequence> Calculator::Tokenize(
360+
absl::string_view expression_body) const {
366361
const char* current = expression_body.data();
367362
const char* end = expression_body.data() + expression_body.size();
368363
int num_operator = 0; // Number of operators or functions appeared
369364
int num_value = 0; // Number of values appeared
370365

371-
DCHECK(tokens);
372-
tokens->clear();
366+
TokenSequence tokens;
373367

374368
while (current < end) {
375369
// Skip spaces
@@ -386,9 +380,9 @@ bool Calculator::Tokenize(absl::string_view expression_body,
386380
std::string number_token(token_begin, current - token_begin);
387381
double value = 0.0;
388382
if (!NumberUtil::SafeStrToDouble(number_token, &value)) {
389-
return false;
383+
return std::nullopt;
390384
}
391-
tokens->push_back({TokenType::INTEGER, value});
385+
tokens.push_back({TokenType::INTEGER, value});
392386
++num_value;
393387
continue;
394388
}
@@ -402,25 +396,25 @@ bool Calculator::Tokenize(absl::string_view expression_body,
402396
absl::AsciiStrToLower(&name);
403397
const auto func_it = operator_map_.find(name);
404398
if (func_it != operator_map_.end()) {
405-
tokens->push_back({func_it->second, 0.0});
399+
tokens.push_back({func_it->second, 0.0});
406400
++num_operator;
407401
continue;
408402
}
409-
return false;
403+
return std::nullopt;
410404
}
411405

412406
// Read operator token
413407
for (size_t length = 1; length <= kMaxLengthOfOperator; ++length) {
414408
if (current + length > end) {
415409
// Invalid token
416-
return false;
410+
return std::nullopt;
417411
}
418412
absl::string_view window(current, length);
419413
const auto op_it = operator_map_.find(window);
420414
if (op_it == operator_map_.end()) {
421415
continue;
422416
}
423-
tokens->push_back({op_it->second, 0.0});
417+
tokens.push_back({op_it->second, 0.0});
424418
current += length;
425419
// Does not count parenthesis as an operator.
426420
if ((op_it->second != TokenType::LP) &&
@@ -434,29 +428,27 @@ bool Calculator::Tokenize(absl::string_view expression_body,
434428
}
435429

436430
// Invalid token
437-
return false;
431+
return std::nullopt;
438432
}
439433

440434
if (num_operator == 0 || num_value == 0) {
441435
// Must contain at least one operator and one value.
442-
return false;
436+
return std::nullopt;
443437
}
444-
return true;
438+
return tokens;
445439
}
446440

447-
bool Calculator::CalculateTokens(const TokenSequence& tokens,
448-
double* result_value) const {
449-
DCHECK(result_value);
441+
std::optional<double> Calculator::CalculateTokens(
442+
const TokenSequence& tokens) const {
450443
ParserContext ctx{tokens, 0};
451444
std::optional<double> result = ParseExpression(ctx);
452445
if (!result.has_value()) {
453-
return false;
446+
return std::nullopt;
454447
}
455448
if (ctx.pos != ctx.tokens.size()) {
456-
return false;
449+
return std::nullopt;
457450
}
458-
*result_value = *result;
459-
return true;
451+
return result;
460452
}
461453

462454
} // namespace mozc

src/rewriter/calculator/calculator.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#define MOZC_REWRITER_CALCULATOR_CALCULATOR_H_
3232

3333
#include <cstddef>
34+
#include <optional>
3435
#include <string>
3536
#include <vector>
3637

@@ -70,22 +71,24 @@ class Calculator {
7071

7172
Calculator();
7273

73-
bool CalculateString(absl::string_view key, std::string* result) const;
74+
std::optional<std::string> CalculateString(absl::string_view key) const;
7475

7576
private:
7677
using TokenSequence = std::vector<Token>;
7778

7879
// Max byte length of operator character
7980
static constexpr size_t kMaxLengthOfOperator = 4;
8081

81-
// Tokenizes |expression_body| and sets the tokens into |tokens|.
82-
// It returns false if |expression_body| includes an invalid token or
82+
// Tokenizes |expression_body| and returns the tokens if the tokenization
83+
// is successful.
84+
// It returns std::nullopt if |expression_body| includes an invalid token or
8385
// does not include both of a number token and an operator token.
8486
// Parenthesis is not considered as an operator.
85-
bool Tokenize(absl::string_view expression_body, TokenSequence* tokens) const;
87+
std::optional<TokenSequence> Tokenize(
88+
absl::string_view expression_body) const;
8689

8790
// Perform calculation with a given sequence of token.
88-
bool CalculateTokens(const TokenSequence& tokens, double* result_value) const;
91+
std::optional<double> CalculateTokens(const TokenSequence& tokens) const;
8992

9093
// Mapping from operator character such as '+' or "log" to the corresponding
9194
// token type.

src/rewriter/calculator/calculator_test.cc

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@
3232
#include <cmath>
3333
#include <cstdlib>
3434
#include <fstream>
35+
#include <optional>
3536
#include <ostream>
3637
#include <string>
3738

3839
#include "absl/log/check.h"
3940
#include "absl/log/log.h"
4041
#include "absl/strings/numbers.h"
4142
#include "absl/strings/string_view.h"
43+
#include "testing/gmock.h"
4244
#include "testing/gunit.h"
4345
#include "testing/mozctest.h"
4446

@@ -49,11 +51,14 @@ namespace {
4951
void VerifyCalculation(const Calculator& calculator,
5052
const absl::string_view expression,
5153
const absl::string_view expected) {
52-
std::string result;
53-
EXPECT_TRUE(calculator.CalculateString(expression, &result))
54+
std::optional<std::string> result = calculator.CalculateString(expression);
55+
EXPECT_TRUE(result.has_value())
5456
<< expression << " expected = " << expected;
57+
if (!result.has_value()) {
58+
return;
59+
}
5560
double result_val;
56-
EXPECT_TRUE(absl::SimpleAtod(result, &result_val));
61+
EXPECT_TRUE(absl::SimpleAtod(*result, &result_val));
5762
double expected_val;
5863
EXPECT_TRUE(absl::SimpleAtod(expected, &expected_val));
5964
const double err = std::fabs(result_val - expected_val);
@@ -62,24 +67,22 @@ void VerifyCalculation(const Calculator& calculator,
6267
<< "comparison: " << result_val << " vs " << expected_val << std::endl
6368
<< "error: " << err << std::endl
6469
<< "expr = " << expression << std::endl
65-
<< "result = " << result;
70+
<< "result = " << *result;
6671
}
6772

6873
// Runs calculation and compare results in PRINTED string.
6974
void VerifyCalculationInString(const Calculator& calculator,
7075
const absl::string_view expression,
7176
const absl::string_view expected) {
72-
std::string result;
73-
EXPECT_TRUE(calculator.CalculateString(expression, &result))
74-
<< expression << " expected = " << expected;
75-
EXPECT_EQ(result, expected) << "expr = " << expression << std::endl;
77+
ASSERT_THAT(calculator.CalculateString(expression),
78+
::testing::Optional(std::string(expected)))
79+
<< "expr = " << expression;
7680
}
7781

7882
// Tries to calculate |wrong_key| and returns true if it fails.
7983
void VerifyRejection(const Calculator& calculator,
8084
const absl::string_view wrong_key) {
81-
std::string result;
82-
EXPECT_FALSE(calculator.CalculateString(wrong_key, &result))
85+
EXPECT_FALSE(calculator.CalculateString(wrong_key).has_value())
8386
<< "expression: " << wrong_key << std::endl;
8487
}
8588

@@ -191,8 +194,8 @@ TEST(CalculatorTest, StressTest) {
191194
// If (__ANDROID__ && x86) the result differs from expectation
192195
// because of floating point specification so on such environment
193196
// Following verification is skipped.
194-
std::string unused_result;
195-
calculator.CalculateString(query, &unused_result);
197+
std::optional<std::string> unused_result =
198+
calculator.CalculateString(query);
196199
#if !defined(__ANDROID__) || !defined(__i386__)
197200
if (line.size() == query_length) {
198201
// False test
@@ -208,15 +211,14 @@ TEST(CalculatorTest, StressTest) {
208211

209212
TEST(CalculatorTest, LimitAndValidationTest) {
210213
Calculator calculator;
211-
std::string unused_result;
212214

213215
// Large but valid operations under limit (e.g. 100 unary operations).
214216
std::string valid_case = std::string(100, '+') + "1=";
215-
EXPECT_TRUE(calculator.CalculateString(valid_case, &unused_result));
217+
EXPECT_TRUE(calculator.CalculateString(valid_case).has_value());
216218

217219
// Directly violating input length limit (e.g. 2100 bytes).
218220
std::string over_length_case = std::string(2100, '+') + "1=";
219-
EXPECT_FALSE(calculator.CalculateString(over_length_case, &unused_result));
221+
EXPECT_FALSE(calculator.CalculateString(over_length_case).has_value());
220222
}
221223

222224
} // namespace mozc

src/rewriter/calculator_rewriter.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ CalculatorRewriter::CheckResizeSegmentsRequest(const ConversionRequest& request,
7979
}
8080
// The decision to calculate and calculation itself are both done by the
8181
// calculator.
82-
std::string result;
83-
if (!calculator_.CalculateString(merged_key, &result)) {
82+
std::optional<std::string> result = calculator_.CalculateString(merged_key);
83+
if (!result.has_value()) {
8484
return std::nullopt;
8585
}
8686

@@ -113,13 +113,13 @@ bool CalculatorRewriter::Rewrite(const ConversionRequest& request,
113113
return false;
114114
}
115115

116-
std::string result;
117-
if (!calculator_.CalculateString(key, &result)) {
116+
std::optional<std::string> result = calculator_.CalculateString(key);
117+
if (!result.has_value()) {
118118
return false;
119119
}
120120

121121
// Insert the result.
122-
if (!InsertCandidate(result, 0, segments->mutable_conversion_segment(0))) {
122+
if (!InsertCandidate(*result, 0, segments->mutable_conversion_segment(0))) {
123123
return false;
124124
}
125125

0 commit comments

Comments
 (0)