From 03dc8f2a13ee3016e50d9f24d88945b075ad07c9 Mon Sep 17 00:00:00 2001 From: deeaitch Date: Mon, 5 Oct 2026 18:59:04 -0400 Subject: [PATCH] avoid lambda to simplify source code --- README.md | 8 +- dbc_decoder.cpp | 11 +- dbc_statement_handler.cpp | 41 ++++---- main.cpp | 5 +- tests/dbc_decoder_test.cpp | 5 +- tests/dbc_parser_test.cpp | 199 +++++++++++++++++++------------------ 6 files changed, 143 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index c0b69a0..a5db857 100644 --- a/README.md +++ b/README.md @@ -425,10 +425,14 @@ produce the `int64_t` alternative and unsigned definitions produce the `uint64_t alternative. Raw values remain exact across their full 64-bit ranges. Check `valid` before using the value; an invalid result's default value is not decoded data. -Use `std::get` when the numeric type is known, or `std::visit` to handle both: +Use `std::get` when the numeric type is known, or `std::get_if` to handle both: ```cpp -std::visit([] (auto raw) { std::cout << raw; }, decodedSignal.rawValue); +if (const auto *value = std::get_if(&decodedSignal.rawValue)) { + std::cout << *value; +} else { + std::cout << std::get(decodedSignal.rawValue); +} ``` This replaces the previous `int64_t` field: consumers that read or print diff --git a/dbc_decoder.cpp b/dbc_decoder.cpp index 00258aa..b2bb4f5 100644 --- a/dbc_decoder.cpp +++ b/dbc_decoder.cpp @@ -57,11 +57,12 @@ DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database, else decoded.rawValue = unsignedValue; - decoded.physicalValue = - std::visit([&signal](auto rawValue) { - return (static_cast(rawValue) * signal.factor) + signal.offset; - }, - decoded.rawValue); + double numericValue; + if (const auto *signedValue = std::get_if(&decoded.rawValue)) + numericValue = static_cast(*signedValue); + else + numericValue = static_cast(std::get(decoded.rawValue)); + decoded.physicalValue = numericValue * signal.factor + signal.offset; decoded.valid = true; result.signals.push_back(decoded); diff --git a/dbc_statement_handler.cpp b/dbc_statement_handler.cpp index 53d726a..beb76a7 100644 --- a/dbc_statement_handler.cpp +++ b/dbc_statement_handler.cpp @@ -20,6 +20,23 @@ namespace { return value; } + double ParsePhysicalValue(const std::string &text, const char *field) { + static const std::regex number( + R"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)"); + if (!std::regex_match(text, number)) + throw std::runtime_error(std::string("Invalid ") + field + ": " + text); + std::size_t consumed = 0U; + double value; + try { + value = std::stod(text, &consumed); + } catch (const std::exception &) { + throw std::runtime_error(std::string("Invalid ") + field + ": " + text); + } + if (consumed != text.size() || !std::isfinite(value)) + throw std::runtime_error(std::string("Invalid ") + field + ": " + text); + return value; + } + /** * @brief Remove leading and trailing spaces. * @param text Input text. @@ -164,26 +181,10 @@ SignalInfo DbcSignalHandler::ParseSignalLine(const std::string &line) { signal.isLittleEndian = fields[4].str() == "1"; signal.isSigned = fields[5].str() == "-"; - const auto parsePhysical = [](const std::string &text, const char *field) { - static const std::regex number( - R"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)"); - if (!std::regex_match(text, number)) - throw std::runtime_error(std::string("Invalid ") + field + ": " + text); - std::size_t consumed = 0U; - double value; - try { - value = std::stod(text, &consumed); - } catch (const std::exception &) { - throw std::runtime_error(std::string("Invalid ") + field + ": " + text); - } - if (consumed != text.size() || !std::isfinite(value)) - throw std::runtime_error(std::string("Invalid ") + field + ": " + text); - return value; - }; - signal.factor = parsePhysical(fields[6].str(), "signal factor"); - signal.offset = parsePhysical(fields[7].str(), "signal offset"); - signal.minimum = parsePhysical(fields[8].str(), "signal minimum"); - signal.maximum = parsePhysical(fields[9].str(), "signal maximum"); + signal.factor = ParsePhysicalValue(fields[6].str(), "signal factor"); + signal.offset = ParsePhysicalValue(fields[7].str(), "signal offset"); + signal.minimum = ParsePhysicalValue(fields[8].str(), "signal minimum"); + signal.maximum = ParsePhysicalValue(fields[9].str(), "signal maximum"); if (signal.minimum > signal.maximum) throw std::runtime_error("Signal minimum exceeds maximum."); signal.unit = fields[10].str(); diff --git a/main.cpp b/main.cpp index f5e129a..f920761 100644 --- a/main.cpp +++ b/main.cpp @@ -88,7 +88,10 @@ static void PrintDecodedFrame(const DecodedFrameValue &decoded) { std::cout << " " << signal.definition->name << " raw="; - std::visit([](auto rawValue) { std::cout << rawValue; }, signal.rawValue); + if (const auto *signedValue = std::get_if(&signal.rawValue)) + std::cout << *signedValue; + else + std::cout << std::get(signal.rawValue); std::cout << " physical=" << signal.physicalValue << " " << signal.definition->unit << "\n"; diff --git a/tests/dbc_decoder_test.cpp b/tests/dbc_decoder_test.cpp index 9ac4cd6..cd3bdd6 100644 --- a/tests/dbc_decoder_test.cpp +++ b/tests/dbc_decoder_test.cpp @@ -118,7 +118,10 @@ int main() { 10.0; std::ostringstream actualText; std::ostringstream expectedText; - std::visit([&actualText](auto raw) { actualText << raw; }, decoded.rawValue); + if (const auto *signedValue = std::get_if(&decoded.rawValue)) + actualText << *signedValue; + else + actualText << std::get(decoded.rawValue); if (type == ValueType::Unsigned) expectedText << bits; else diff --git a/tests/dbc_parser_test.cpp b/tests/dbc_parser_test.cpp index c0a54ca..9fe0450 100644 --- a/tests/dbc_parser_test.cpp +++ b/tests/dbc_parser_test.cpp @@ -33,17 +33,41 @@ public: } }; -int main() { - Fixture fixture; +std::string SignalLine(const std::string &start, const std::string &length, + const std::string &order, const std::string &factor, + const std::string &offset, const std::string &minimum, + const std::string &maximum) { + return "SG_ Value : " + start + "|" + length + "@" + order + + " (" + factor + "," + offset + ") [" + minimum + "|" + maximum + "] \"\" ECU"; +} + +struct TestContext { int failures = 0; std::size_t count = 0U; - const auto check = [&](bool passed, const std::string &name) { + void Check(bool passed, const std::string &name) { ++count; if (!passed) { ++failures; std::cerr << "FAIL: " << name << '\n'; } - }; + } + void RejectSignal(const Fixture &fixture, const std::string &line, unsigned payload = 8U) { + fixture.Write("BO_ 1 Frame: " + std::to_string(payload) + " ECU\n\n" + line + "\n"); + try { + DbcParser().ParseFile(fixture.path.string()); + Check(false, "Reject " + line); + } catch (const std::runtime_error &error) { + const std::string prefix = fixture.path.string() + ":3: "; + Check(std::string(error.what()).find(prefix) == 0U && + std::string(error.what()).size() > prefix.size(), + "Reject " + line); + } + } +}; + +int main() { + Fixture fixture; + TestContext tests; struct ValidCase { const char *line; std::uint32_t id; @@ -62,13 +86,13 @@ int main() { fixture.Write(std::string(test.line) + '\n'); try { const auto database = DbcParser().ParseFile(fixture.path.string()); - check(database.frames.size() == 1U && database.frames[0].name == "Frame" && - database.frames[0].canId == test.id && - database.frames[0].isExtended == test.extended && - database.frames[0].dlc == test.length && - database.frames[0].transmitter == "ECU", - test.line); - } catch (const std::exception &error) { check(false, error.what()); } + tests.Check(database.frames.size() == 1U && database.frames[0].name == "Frame" && + database.frames[0].canId == test.id && + database.frames[0].isExtended == test.extended && + database.frames[0].dlc == test.length && + database.frames[0].transmitter == "ECU", + test.line); + } catch (const std::exception &error) { tests.Check(false, error.what()); } } const char *invalid[] = { "BO_ invalid", "BO_", "BO_ 1", "BO_ 1 : 8 ECU", "BO_ 1 Frame 8 ECU", @@ -85,12 +109,12 @@ int main() { fixture.Write(std::string("VERSION \"test\"\n\n") + line + '\n'); try { DbcParser().ParseFile(fixture.path.string()); - check(false, line); + tests.Check(false, line); } catch (const std::runtime_error &error) { const std::string prefix = fixture.path.string() + ":3: "; - check(std::string(error.what()).find(prefix) == 0U && - std::string(error.what()).size() > prefix.size(), - line); + tests.Check(std::string(error.what()).find(prefix) == 0U && + std::string(error.what()).size() > prefix.size(), + line); } } fixture.Write( @@ -99,84 +123,65 @@ int main() { "CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\n" "BO_ 257 Second: 8 ECU\n"); const auto database = DbcParser().ParseFile(fixture.path.string()); - check(database.frames.size() == 2U && database.frames[0].signals.size() == 1U && - database.frames[0].comment == "Frame comment" && - database.frames[0].signals[0].comment == "Signal comment", - "Integration"); + tests.Check(database.frames.size() == 2U && database.frames[0].signals.size() == 1U && + database.frames[0].comment == "Frame comment" && + database.frames[0].signals[0].comment == "Signal comment", + "Integration"); fixture.Write("\nSG_ Speed : invalid\n"); try { DbcParser().ParseFile(fixture.path.string()); - check(false, "Signal error location"); + tests.Check(false, "Signal error location"); } catch (const std::runtime_error &error) { - check(std::string(error.what()).find(fixture.path.string() + ":2: ") == 0U, - "Signal error location"); + tests.Check(std::string(error.what()).find(fixture.path.string() + ":2: ") == 0U, + "Signal error location"); } const DbcParser signalParser; - const auto signalLine = [](const std::string &start, const std::string &length, - const std::string &order, const std::string &factor, - const std::string &offset, const std::string &minimum, - const std::string &maximum) { - return "SG_ Value : " + start + "|" + length + "@" + order + - " (" + factor + "," + offset + ") [" + minimum + "|" + maximum + "] \"\" ECU"; - }; - const auto rejectSignal = [&](const std::string &line, unsigned payload = 8U) { - fixture.Write("BO_ 1 Frame: " + std::to_string(payload) + " ECU\n\n" + line + "\n"); - try { - signalParser.ParseFile(fixture.path.string()); - check(false, "Reject " + line); - } catch (const std::runtime_error &error) { - const std::string prefix = fixture.path.string() + ":3: "; - check(std::string(error.what()).find(prefix) == 0U && - std::string(error.what()).size() > prefix.size(), - "Reject " + line); - } - }; - const std::string ordinary = signalLine("0", "8", "1+", "1", "0", "0", "255"); + const std::string ordinary = SignalLine("0", "8", "1+", "1", "0", "0", "255"); for (const std::string value: {"-1", "+1", "0junk", "0x10", "1.5", "4294967296", "999999999999999999999"}) { - rejectSignal(signalLine(value, "8", "1+", "1", "0", "0", "255")); - rejectSignal(signalLine("0", value, "1+", "1", "0", "0", "255")); + tests.RejectSignal(fixture, SignalLine(value, "8", "1+", "1", "0", "0", "255")); + tests.RejectSignal(fixture, SignalLine("0", value, "1+", "1", "0", "0", "255")); } for (const std::string length: {"0", "65"}) - rejectSignal(signalLine("0", length, "1+", "1", "0", "0", "255")); + tests.RejectSignal(fixture, SignalLine("0", length, "1+", "1", "0", "0", "255")); for (const std::string order: {"2+", "9-", "1", "1x", "1++", "01+"}) - rejectSignal(signalLine("0", "8", order, "1", "0", "0", "255")); + tests.RejectSignal(fixture, SignalLine("0", "8", order, "1", "0", "0", "255")); for (const std::string value: {"1junk", "nan", "NaN", "inf", "-inf", "1e999", "1e-999", "0x1p2", "", "1e", "1.2.3"}) { - rejectSignal(signalLine("0", "8", "1+", value, "0", "0", "255")); - rejectSignal(signalLine("0", "8", "1+", "1", value, "0", "255")); - rejectSignal(signalLine("0", "8", "1+", "1", "0", value, "255")); - rejectSignal(signalLine("0", "8", "1+", "1", "0", "0", value)); + tests.RejectSignal(fixture, SignalLine("0", "8", "1+", value, "0", "0", "255")); + tests.RejectSignal(fixture, SignalLine("0", "8", "1+", "1", value, "0", "255")); + tests.RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", value, "255")); + tests.RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "0", value)); } - rejectSignal(signalLine("0", "8", "1+", "1", "0", "2", "1")); + tests.RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "2", "1")); for (const std::string name: {"", "Two Names", "Value M", "Value m1", "Value m1M"}) { std::string line = ordinary; line.replace(4U, 5U, name); - rejectSignal(line); + tests.RejectSignal(fixture, line); } for (const std::string broken: { "SG_", "SG_ Value", "SG_ Value : 0|8@1+ (1,0) [0|255] ECU", "SG_ Value : 0|8@1+ (1,0) [0|255] \"\"", "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU,", "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU extra"}) - rejectSignal(broken); - rejectSignal(signalLine("64", "1", "1+", "1", "0", "0", "1")); - rejectSignal(signalLine("63", "2", "1+", "1", "0", "0", "3")); - rejectSignal(signalLine("56", "2", "0+", "1", "0", "0", "3")); - rejectSignal(signalLine("4294967295", "64", "1+", "1", "0", "0", "1")); - rejectSignal(ordinary, 0U); + tests.RejectSignal(fixture, broken); + tests.RejectSignal(fixture, SignalLine("64", "1", "1+", "1", "0", "0", "1")); + tests.RejectSignal(fixture, SignalLine("63", "2", "1+", "1", "0", "0", "3")); + tests.RejectSignal(fixture, SignalLine("56", "2", "0+", "1", "0", "0", "3")); + tests.RejectSignal(fixture, SignalLine("4294967295", "64", "1+", "1", "0", "0", "1")); + tests.RejectSignal(fixture, ordinary, 0U); for (const char delimiter: {'|', '@', '(', ')', '[', ']', '"'}) { std::string line = ordinary; line.erase(line.find(delimiter), 1U); - rejectSignal(line); + tests.RejectSignal(fixture, line); } fixture.Write("BO_ 1 Frame: 8 ECU\n" + - signalLine("0", "8", "1+", "0", "0", "0", "0") + "\n" + - signalLine("8", "8", "1-", "-1", "0", "-255", "0") + "\n"); + SignalLine("0", "8", "1+", "0", "0", "0", "0") + "\n" + + SignalLine("8", "8", "1-", "-1", "0", "-255", "0") + "\n"); const auto factors = signalParser.ParseFile(fixture.path.string()); - check(factors.frames[0].signals.size() == 2U && - factors.frames[0].signals[0].factor == 0.0 && - factors.frames[0].signals[1].factor == -1.0, - "Zero and negative factors and equal range boundaries"); + tests.Check(factors.frames[0].signals.size() == 2U && + factors.frames[0].signals[0].factor == 0.0 && + factors.frames[0].signals[1].factor == -1.0, + "Zero and negative factors and equal range boundaries"); struct SignalCase { const char *start; @@ -195,59 +200,59 @@ int main() { {"2039", "1", "1+", 255U}}; for (const auto &test: validSignals) { fixture.Write("BO_ 1 Frame: " + std::to_string(test.payload) + " ECU\n" + - signalLine(test.start, test.length, test.order, ".5e+1", "-2.5E-1", "-1.", "+2e2") + "\n"); + SignalLine(test.start, test.length, test.order, ".5e+1", "-2.5E-1", "-1.", "+2e2") + "\n"); try { const auto parsed = signalParser.ParseFile(fixture.path.string()); const auto &signal = parsed.frames.at(0).signals.at(0); - check(signal.startBit == std::stoul(test.start) && - signal.length == std::stoul(test.length) && - signal.isLittleEndian == (test.order[0] == '1') && - signal.isSigned == (test.order[1] == '-') && - signal.factor == 5.0 && signal.offset == -0.25 && - signal.minimum == -1.0 && signal.maximum == 200.0, - "Valid signal boundary and numeric metadata"); - } catch (const std::exception &error) { check(false, error.what()); } + tests.Check(signal.startBit == std::stoul(test.start) && + signal.length == std::stoul(test.length) && + signal.isLittleEndian == (test.order[0] == '1') && + signal.isSigned == (test.order[1] == '-') && + signal.factor == 5.0 && signal.offset == -0.25 && + signal.minimum == -1.0 && signal.maximum == 200.0, + "Valid signal boundary and numeric metadata"); + } catch (const std::exception &error) { tests.Check(false, error.what()); } } fixture.Write("BO_ 1 Frame: 8 ECU\n" "SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n"); try { const auto parsed = signalParser.ParseFile(fixture.path.string()); const auto &signal = parsed.frames.at(0).signals.at(0); - check(signal.name == "Value" && signal.unit == "rpm" && - signal.receivers == std::vector({"ECU1", "ECU2"}), - "Signal tabs, spaces, and receiver list"); - } catch (const std::exception &error) { check(false, error.what()); } + tests.Check(signal.name == "Value" && signal.unit == "rpm" && + signal.receivers == std::vector({"ECU1", "ECU2"}), + "Signal tabs, spaces, and receiver list"); + } catch (const std::exception &error) { tests.Check(false, error.what()); } DbcDatabase dispatchDatabase; DbcParseContext context{dispatchDatabase, nullptr}; DbcStatementDispatcher dispatcher; dispatcher.RegisterType("TEST_"); - check(dispatcher.Dispatch("TEST_ first", context) && - dispatcher.Dispatch("TEST_ second", context) && - CountingHandler::instances == 1 && CountingHandler::calls == 2 && - dispatchDatabase.frames.size() == 2U, - "Factory dispatch and handler reuse"); - check(!dispatcher.Dispatch("TEST_OTHER data", context) && - !dispatcher.Dispatch("BO_TX_BU_ 1 : ECU;", context) && - !dispatcher.Dispatch("VAL_ 1 Speed 0 \"Stopped\";", context) && - CountingHandler::calls == 2, - "Exact and unknown keyword dispatch"); + tests.Check(dispatcher.Dispatch("TEST_ first", context) && + dispatcher.Dispatch("TEST_ second", context) && + CountingHandler::instances == 1 && CountingHandler::calls == 2 && + dispatchDatabase.frames.size() == 2U, + "Factory dispatch and handler reuse"); + tests.Check(!dispatcher.Dispatch("TEST_OTHER data", context) && + !dispatcher.Dispatch("BO_TX_BU_ 1 : ECU;", context) && + !dispatcher.Dispatch("VAL_ 1 Speed 0 \"Stopped\";", context) && + CountingHandler::calls == 2, + "Exact and unknown keyword dispatch"); const DbcParser parser; fixture.Write("BO_ 1 First: 8 ECU\n"); - check(parser.ParseFile(fixture.path.string()).frames.size() == 1U, "First parse"); + tests.Check(parser.ParseFile(fixture.path.string()).frames.size() == 1U, "First parse"); fixture.Write("SG_ Speed : invalid\n"); try { parser.ParseFile(fixture.path.string()); - check(false, "Independent parse context"); + tests.Check(false, "Independent parse context"); } catch (const std::runtime_error &error) { - check(std::string(error.what()).find("Signal found before any frame") != - std::string::npos, - "Independent parse context"); + tests.Check(std::string(error.what()).find("Signal found before any frame") != + std::string::npos, + "Independent parse context"); } fixture.Write("BO_ 2 Second: 8 ECU\n"); const auto recovered = parser.ParseFile(fixture.path.string()); - check(recovered.frames.size() == 1U && recovered.frames[0].canId == 2U, - "Fresh parse after error"); - if (failures != 0) return 1; - std::cout << "All " << count << " parser cases passed.\n"; + tests.Check(recovered.frames.size() == 1U && recovered.frames[0].canId == 2U, + "Fresh parse after error"); + if (tests.failures != 0) return 1; + std::cout << "All " << tests.count << " parser cases passed.\n"; return 0; }