This commit is contained in:
deeaitch committed 2026-10-05 18:37:20 -04:00
1 parent 8d82b13dad
commit b91cf4aae5
20 files changed
+510 -491

No files matched your search

+65
View File
@@ -0,0 +1,65 @@
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: None
AlignOperands: Align
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Always
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: true
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
ColumnLimit: 0
CompactNamespaces: false
ContinuationIndentWidth: 8
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PointerAlignment: Right
ReflowComments: false
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 0
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Never
+4 -4
View File
@@ -11,7 +11,7 @@
#include "dbc_decode_builder.h" #include "dbc_decode_builder.h"
DecodeDatabase DbcDecodeBuilder::Build (const DbcDatabase &source) const { DecodeDatabase DbcDecodeBuilder::Build(const DbcDatabase &source) const {
DecodeDatabase result; DecodeDatabase result;
for (std::size_t frameIndex = 0U; frameIndex < source.frames.size(); ++frameIndex) { for (std::size_t frameIndex = 0U; frameIndex < source.frames.size(); ++frameIndex) {
@@ -44,11 +44,11 @@ DecodeDatabase DbcDecodeBuilder::Build (const DbcDatabase &source) const {
targetSignal.receivers = sourceSignal.receivers; targetSignal.receivers = sourceSignal.receivers;
targetSignal.comment = sourceSignal.comment; targetSignal.comment = sourceSignal.comment;
targetFrame.signals.push_back (targetSignal); targetFrame.signals.push_back(targetSignal);
} }
result.frames.push_back (targetFrame); result.frames.push_back(targetFrame);
result.frameIndexByKey[FrameKey (targetFrame.canId, targetFrame.isExtended)] = result.frameIndexByKey[FrameKey(targetFrame.canId, targetFrame.isExtended)] =
result.frames.size() - 1U; result.frames.size() - 1U;
} }
+2 -2
View File
@@ -19,13 +19,13 @@
* @brief Converts parsed DBC data into runtime decode database. * @brief Converts parsed DBC data into runtime decode database.
*/ */
class DbcDecodeBuilder { class DbcDecodeBuilder {
public: public:
/** /**
* @brief Build runtime decode database. * @brief Build runtime decode database.
* @param source Parsed DBC database. * @param source Parsed DBC database.
* @return Runtime-ready decode database. * @return Runtime-ready decode database.
*/ */
DecodeDatabase Build (const DbcDatabase &source) const; DecodeDatabase Build(const DbcDatabase &source) const;
}; };
#endif /* DBC_DECODE_BUILDER_H */ #endif /* DBC_DECODE_BUILDER_H */
+26 -24
View File
@@ -11,12 +11,12 @@
#include "dbc_decoder.h" #include "dbc_decoder.h"
const DecodeFrame *DbcDecoder::FindFrame (const DecodeDatabase &database, const DecodeFrame *DbcDecoder::FindFrame(const DecodeDatabase &database,
std::uint32_t canId, std::uint32_t canId,
bool isExtended) const { bool isExtended) const {
const FrameKey key (canId, isExtended); const FrameKey key(canId, isExtended);
const std::unordered_map<FrameKey, std::size_t, FrameKeyHasher>::const_iterator it = const std::unordered_map<FrameKey, std::size_t, FrameKeyHasher>::const_iterator it =
database.frameIndexByKey.find (key); database.frameIndexByKey.find(key);
if (it == database.frameIndexByKey.end()) if (it == database.frameIndexByKey.end())
return nullptr; return nullptr;
@@ -28,10 +28,10 @@ const DecodeFrame *DbcDecoder::FindFrame (const DecodeDatabase &database,
return &database.frames[index]; return &database.frames[index];
} }
DecodedFrameValue DbcDecoder::Decode (const DecodeDatabase &database, DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database,
const RawCanFrame &frame) const { const RawCanFrame &frame) const {
DecodedFrameValue result; DecodedFrameValue result;
const DecodeFrame *definition = FindFrame (database, frame.canId, frame.isExtended); const DecodeFrame *definition = FindFrame(database, frame.canId, frame.isExtended);
if (definition == nullptr) if (definition == nullptr)
return result; return result;
@@ -46,42 +46,43 @@ DecodedFrameValue DbcDecoder::Decode (const DecodeDatabase &database,
decoded.definition = &signal; decoded.definition = &signal;
if (!ExtractUnsigned (frame.data, signal, unsignedValue)) { if (!ExtractUnsigned(frame.data, signal, unsignedValue)) {
decoded.valid = false; decoded.valid = false;
result.signals.push_back (decoded); result.signals.push_back(decoded);
continue; continue;
} }
if (signal.valueType == ValueType::Signed) if (signal.valueType == ValueType::Signed)
decoded.rawValue = SignExtend (unsignedValue, signal.length); decoded.rawValue = SignExtend(unsignedValue, signal.length);
else else
decoded.rawValue = unsignedValue; decoded.rawValue = unsignedValue;
decoded.physicalValue = decoded.physicalValue =
std::visit ([&signal] (auto rawValue) { std::visit([&signal](auto rawValue) {
return (static_cast<double> (rawValue) * signal.factor) + signal.offset; return (static_cast<double>(rawValue) * signal.factor) + signal.offset;
}, decoded.rawValue); },
decoded.rawValue);
decoded.valid = true; decoded.valid = true;
result.signals.push_back (decoded); result.signals.push_back(decoded);
} }
return result; return result;
} }
bool DbcDecoder::ExtractUnsigned (const std::vector<std::uint8_t> &data, bool DbcDecoder::ExtractUnsigned(const std::vector<std::uint8_t> &data,
const DecodeSignal &signal, const DecodeSignal &signal,
std::uint64_t &value) { std::uint64_t &value) {
if ((signal.length == 0U) || (signal.length > 64U)) if ((signal.length == 0U) || (signal.length > 64U))
return false; return false;
if (signal.byteOrder == ByteOrder::Intel) if (signal.byteOrder == ByteOrder::Intel)
return ExtractIntel (data, signal.startBit, signal.length, value); return ExtractIntel(data, signal.startBit, signal.length, value);
return ExtractMotorola (data, signal.startBit, signal.length, value); return ExtractMotorola(data, signal.startBit, signal.length, value);
} }
bool DbcDecoder::ExtractIntel (const std::vector<std::uint8_t> &data, bool DbcDecoder::ExtractIntel(const std::vector<std::uint8_t> &data,
std::uint32_t startBit, std::uint32_t startBit,
std::uint32_t length, std::uint32_t length,
std::uint64_t &value) { std::uint64_t &value) {
@@ -96,7 +97,7 @@ bool DbcDecoder::ExtractIntel (const std::vector<std::uint8_t> &data,
return false; return false;
const std::uint64_t bitValue = const std::uint64_t bitValue =
(static_cast<std::uint64_t> ((data[byteIndex] >> bitInByte) & 0x01U) << bitIndex); (static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U) << bitIndex);
value |= bitValue; value |= bitValue;
} }
@@ -104,7 +105,7 @@ bool DbcDecoder::ExtractIntel (const std::vector<std::uint8_t> &data,
return true; return true;
} }
bool DbcDecoder::ExtractMotorola (const std::vector<std::uint8_t> &data, bool DbcDecoder::ExtractMotorola(const std::vector<std::uint8_t> &data,
std::uint32_t startBit, std::uint32_t startBit,
std::uint32_t length, std::uint32_t length,
std::uint64_t &value) { std::uint64_t &value) {
@@ -126,7 +127,7 @@ bool DbcDecoder::ExtractMotorola (const std::vector<std::uint8_t> &data,
return false; return false;
value <<= 1U; value <<= 1U;
value |= static_cast<std::uint64_t> ((data[byteIndex] >> bitInByte) & 0x01U); value |= static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U);
if (bitInByte == 0U) { if (bitInByte == 0U) {
++byteIndex; ++byteIndex;
@@ -138,18 +139,19 @@ bool DbcDecoder::ExtractMotorola (const std::vector<std::uint8_t> &data,
return true; return true;
} }
std::int64_t DbcDecoder::SignExtend (std::uint64_t value, std::uint32_t bitLength) { std::int64_t DbcDecoder::SignExtend(std::uint64_t value, std::uint32_t bitLength) {
if ((bitLength == 0U) || (bitLength > 64U)) if ((bitLength == 0U) || (bitLength > 64U))
return 0; return 0;
const std::uint64_t signMask = std::uint64_t (1U) << (bitLength - 1U); const std::uint64_t signMask = std::uint64_t(1U) << (bitLength - 1U);
const std::uint64_t valueMask = (bitLength == 64U) const std::uint64_t valueMask = (bitLength == 64U)
? ~std::uint64_t (0U) : (std::uint64_t (1U) << bitLength) - 1U; ? ~std::uint64_t(0U)
: (std::uint64_t(1U) << bitLength) - 1U;
value &= valueMask; value &= valueMask;
if ((value & signMask) == 0U) if ((value & signMask) == 0U)
return static_cast<std::int64_t> (value); return static_cast<std::int64_t>(value);
// The complemented magnitude fits int64_t, including for INT64_MIN. // The complemented magnitude fits int64_t, including for INT64_MIN.
return -1 - static_cast<std::int64_t> ((~value) & valueMask); return -1 - static_cast<std::int64_t>((~value) & valueMask);
} }
+12 -19
View File
@@ -12,9 +12,9 @@
#ifndef DBC_DECODER_H #ifndef DBC_DECODER_H
#define DBC_DECODER_H #define DBC_DECODER_H
#include <vector>
#include <cstdint> #include <cstdint>
#include <variant> #include <variant>
#include <vector>
#include "decode_database.h" #include "decode_database.h"
@@ -27,9 +27,7 @@ struct RawCanFrame {
std::vector<std::uint8_t> data; /**< Payload bytes. */ std::vector<std::uint8_t> data; /**< Payload bytes. */
RawCanFrame() RawCanFrame()
: canId (0U) : canId(0U), isExtended(false), data() {
, isExtended (false)
, data() {
} }
}; };
@@ -43,10 +41,7 @@ struct DecodedSignalValue {
bool valid; /**< true if decoding succeeded. */ bool valid; /**< true if decoding succeeded. */
DecodedSignalValue() DecodedSignalValue()
: definition (nullptr) : definition(nullptr), rawValue(std::uint64_t(0U)), physicalValue(0.0), valid(false) {
, rawValue (std::uint64_t (0U))
, physicalValue (0.0)
, valid (false) {
} }
}; };
@@ -59,9 +54,7 @@ struct DecodedFrameValue {
bool valid; /**< true if frame was matched. */ bool valid; /**< true if frame was matched. */
DecodedFrameValue() DecodedFrameValue()
: definition (nullptr) : definition(nullptr), signals(), valid(false) {
, signals()
, valid (false) {
} }
}; };
@@ -69,7 +62,7 @@ struct DecodedFrameValue {
* @brief Runtime CAN decoder using prebuilt decode database. * @brief Runtime CAN decoder using prebuilt decode database.
*/ */
class DbcDecoder { class DbcDecoder {
public: public:
/** /**
* @brief Find frame definition by CAN ID. * @brief Find frame definition by CAN ID.
* @param database Runtime decode database. * @param database Runtime decode database.
@@ -77,7 +70,7 @@ class DbcDecoder {
* @param isExtended true for extended frame. * @param isExtended true for extended frame.
* @return Pointer to frame definition or nullptr. * @return Pointer to frame definition or nullptr.
*/ */
const DecodeFrame *FindFrame (const DecodeDatabase &database, const DecodeFrame *FindFrame(const DecodeDatabase &database,
std::uint32_t canId, std::uint32_t canId,
bool isExtended) const; bool isExtended) const;
@@ -87,25 +80,25 @@ class DbcDecoder {
* @param frame Raw CAN frame. * @param frame Raw CAN frame.
* @return Decoded frame value. * @return Decoded frame value.
*/ */
DecodedFrameValue Decode (const DecodeDatabase &database, DecodedFrameValue Decode(const DecodeDatabase &database,
const RawCanFrame &frame) const; const RawCanFrame &frame) const;
private: private:
static bool ExtractUnsigned (const std::vector<std::uint8_t> &data, static bool ExtractUnsigned(const std::vector<std::uint8_t> &data,
const DecodeSignal &signal, const DecodeSignal &signal,
std::uint64_t &value); std::uint64_t &value);
static bool ExtractIntel (const std::vector<std::uint8_t> &data, static bool ExtractIntel(const std::vector<std::uint8_t> &data,
std::uint32_t startBit, std::uint32_t startBit,
std::uint32_t length, std::uint32_t length,
std::uint64_t &value); std::uint64_t &value);
static bool ExtractMotorola (const std::vector<std::uint8_t> &data, static bool ExtractMotorola(const std::vector<std::uint8_t> &data,
std::uint32_t startBit, std::uint32_t startBit,
std::uint32_t length, std::uint32_t length,
std::uint64_t &value); std::uint64_t &value);
static std::int64_t SignExtend (std::uint64_t value, std::uint32_t bitLength); static std::int64_t SignExtend(std::uint64_t value, std::uint32_t bitLength);
}; };
#endif /* DBC_DECODER_H */ #endif /* DBC_DECODER_H */
+16 -16
View File
@@ -12,9 +12,9 @@
#include "dbc_parser.h" #include "dbc_parser.h"
#include "dbc_statement_handler.h" #include "dbc_statement_handler.h"
#include <cctype>
#include <fstream> #include <fstream>
#include <stdexcept> #include <stdexcept>
#include <cctype>
namespace { namespace {
/** /**
@@ -22,42 +22,42 @@ namespace {
* @param text Input text. * @param text Input text.
* @return Trimmed text. * @return Trimmed text.
*/ */
std::string TrimText (const std::string &text) { std::string TrimText(const std::string &text) {
std::string::size_type begin = 0U; std::string::size_type begin = 0U;
while ((begin < text.size()) && while ((begin < text.size()) &&
std::isspace (static_cast<unsigned char> (text[begin]))) std::isspace(static_cast<unsigned char>(text[begin])))
++begin; ++begin;
std::string::size_type end = text.size(); std::string::size_type end = text.size();
while ((end > begin) && while ((end > begin) &&
std::isspace (static_cast<unsigned char> (text[end - 1U]))) std::isspace(static_cast<unsigned char>(text[end - 1U])))
--end; --end;
return text.substr (begin, end - begin); return text.substr(begin, end - begin);
} }
} }// namespace
DbcDatabase DbcParser::ParseFile (const std::string &filePath) const { DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
std::ifstream input (filePath.c_str()); std::ifstream input(filePath.c_str());
if (!input.is_open()) if (!input.is_open())
throw std::runtime_error ("Failed to open DBC file: " + filePath); throw std::runtime_error("Failed to open DBC file: " + filePath);
DbcDatabase database; DbcDatabase database;
DbcParseContext context {database, nullptr}; DbcParseContext context{database, nullptr};
const DbcStatementDispatcher dispatcher; const DbcStatementDispatcher dispatcher;
std::size_t lineNumber = 0U; std::size_t lineNumber = 0U;
std::string line; std::string line;
while (std::getline (input, line)) { while (std::getline(input, line)) {
++lineNumber; ++lineNumber;
line = Trim (line); line = Trim(line);
if (line.empty()) if (line.empty())
continue; continue;
try { try {
dispatcher.Dispatch (line, context); dispatcher.Dispatch(line, context);
} catch (const std::exception &error) { } catch (const std::exception &error) {
throw std::runtime_error (filePath + ":" + std::to_string (lineNumber) + throw std::runtime_error(filePath + ":" + std::to_string(lineNumber) +
": " + error.what()); ": " + error.what());
} }
} }
@@ -65,6 +65,6 @@ DbcDatabase DbcParser::ParseFile (const std::string &filePath) const {
return database; return database;
} }
std::string DbcParser::Trim (const std::string &text) { std::string DbcParser::Trim(const std::string &text) {
return TrimText (text); return TrimText(text);
} }
+4 -4
View File
@@ -31,7 +31,7 @@
* - value tables * - value tables
*/ */
class DbcParser { class DbcParser {
public: public:
/** /**
* @brief Parse DBC file. * @brief Parse DBC file.
* @param filePath Path to input file. * @param filePath Path to input file.
@@ -39,10 +39,10 @@ class DbcParser {
* @throws std::runtime_error on file or parse errors. * @throws std::runtime_error on file or parse errors.
* Line parse errors include the file path and one-based line number. * Line parse errors include the file path and one-based line number.
*/ */
DbcDatabase ParseFile (const std::string &filePath) const; DbcDatabase ParseFile(const std::string &filePath) const;
private: private:
static std::string Trim (const std::string &text); static std::string Trim(const std::string &text);
}; };
#endif /* DBC_PARSER_H */ #endif /* DBC_PARSER_H */
+99 -99
View File
@@ -1,22 +1,22 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
#include "dbc_statement_handler.h" #include "dbc_statement_handler.h"
#include <sstream>
#include <stdexcept>
#include <cctype> #include <cctype>
#include <charconv> #include <charconv>
#include <cmath>
#include <limits> #include <limits>
#include <regex> #include <regex>
#include <cmath> #include <sstream>
#include <stdexcept>
namespace { namespace {
std::uint32_t ParseDecimal (const std::string &text, std::uint32_t ParseDecimal(const std::string &text,
std::uint32_t maximum, std::uint32_t maximum,
const char *field) { const char *field) {
std::uint32_t value = 0U; std::uint32_t value = 0U;
const auto parsed = std::from_chars (text.data(), text.data() + text.size(), value); const auto parsed = std::from_chars(text.data(), text.data() + text.size(), value);
if (text.empty() || parsed.ec != std::errc() || if (text.empty() || parsed.ec != std::errc() ||
parsed.ptr != text.data() + text.size() || value > maximum) parsed.ptr != text.data() + text.size() || value > maximum)
throw std::runtime_error (std::string ("Invalid ") + field + ": " + text); throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
return value; return value;
} }
@@ -25,39 +25,39 @@ namespace {
* @param text Input text. * @param text Input text.
* @return Trimmed text. * @return Trimmed text.
*/ */
std::string TrimText (const std::string &text) { std::string TrimText(const std::string &text) {
std::string::size_type begin = 0U; std::string::size_type begin = 0U;
while ((begin < text.size()) && while ((begin < text.size()) &&
std::isspace (static_cast<unsigned char> (text[begin]))) std::isspace(static_cast<unsigned char>(text[begin])))
++begin; ++begin;
std::string::size_type end = text.size(); std::string::size_type end = text.size();
while ((end > begin) && while ((end > begin) &&
std::isspace (static_cast<unsigned char> (text[end - 1U]))) std::isspace(static_cast<unsigned char>(text[end - 1U])))
--end; --end;
return text.substr (begin, end - begin); return text.substr(begin, end - begin);
} }
FrameInfo *FindFrameById (DbcDatabase &, std::uint32_t, bool); FrameInfo *FindFrameById(DbcDatabase &, std::uint32_t, bool);
SignalInfo *FindSignalByName (FrameInfo &, const std::string &); SignalInfo *FindSignalByName(FrameInfo &, const std::string &);
} }// namespace
namespace { namespace {
std::vector<std::string> SplitReceivers (const std::string &text) { std::vector<std::string> SplitReceivers(const std::string &text) {
std::vector<std::string> receivers; std::vector<std::string> receivers;
std::string token; std::string token;
std::istringstream stream (text); std::istringstream stream(text);
while (std::getline (stream, token, ',')) { while (std::getline(stream, token, ',')) {
token = TrimText (token); token = TrimText(token);
if (!token.empty()) if (!token.empty())
receivers.push_back (token); receivers.push_back(token);
} }
return receivers; return receivers;
} }
void NormalizeCanId (std::uint32_t rawCanId, void NormalizeCanId(std::uint32_t rawCanId,
std::uint32_t &normalizedCanId, std::uint32_t &normalizedCanId,
bool &isExtended) { bool &isExtended) {
/* /*
@@ -72,9 +72,9 @@ void NormalizeCanId (std::uint32_t rawCanId,
isExtended = (rawCanId > 0x7FFU); isExtended = (rawCanId > 0x7FFU);
normalizedCanId = rawCanId; normalizedCanId = rawCanId;
} }
} }
std::uint32_t TryExtractPgn (std::uint32_t canId, bool isExtended, bool &hasPgn) { std::uint32_t TryExtractPgn(std::uint32_t canId, bool isExtended, bool &hasPgn) {
hasPgn = false; hasPgn = false;
if (!isExtended) if (!isExtended)
@@ -96,109 +96,109 @@ std::uint32_t TryExtractPgn (std::uint32_t canId, bool isExtended, bool &hasPgn)
hasPgn = true; hasPgn = true;
return pgn; return pgn;
} }
} }// namespace
FrameInfo DbcFrameHandler::ParseFrameLine (const std::string &line) { FrameInfo DbcFrameHandler::ParseFrameLine(const std::string &line) {
/* /*
* Example: * Example:
* BO_ 256 EngineData: 8 EEC1 * BO_ 256 EngineData: 8 EEC1
*/ */
std::istringstream stream (line); std::istringstream stream(line);
std::string keyword; std::string keyword;
std::string idText; std::string idText;
if (!(stream >> keyword >> idText) || keyword != "BO_") if (!(stream >> keyword >> idText) || keyword != "BO_")
throw std::runtime_error ("Missing CAN ID in frame definition."); throw std::runtime_error("Missing CAN ID in frame definition.");
const std::uint32_t rawCanId = ParseDecimal ( const std::uint32_t rawCanId = ParseDecimal(
idText, std::numeric_limits<std::uint32_t>::max(), "CAN ID"); idText, std::numeric_limits<std::uint32_t>::max(), "CAN ID");
// Only bit 31 may accompany the actual 29-bit identifier. // Only bit 31 may accompany the actual 29-bit identifier.
if ((rawCanId & 0x60000000U) != 0U) if ((rawCanId & 0x60000000U) != 0U)
throw std::runtime_error ("CAN ID exceeds the 29-bit identifier range."); throw std::runtime_error("CAN ID exceeds the 29-bit identifier range.");
std::string nameText; std::string nameText;
if (!std::getline (stream, nameText, ':') || stream.eof()) if (!std::getline(stream, nameText, ':') || stream.eof())
throw std::runtime_error ("Missing ':' in frame definition."); throw std::runtime_error("Missing ':' in frame definition.");
std::istringstream nameStream (nameText); std::istringstream nameStream(nameText);
FrameInfo frame; FrameInfo frame;
std::string extra; std::string extra;
if (!(nameStream >> frame.name) || (nameStream >> extra)) if (!(nameStream >> frame.name) || (nameStream >> extra))
throw std::runtime_error ("Missing or invalid frame name."); throw std::runtime_error("Missing or invalid frame name.");
std::string lengthText; std::string lengthText;
if (!(stream >> lengthText)) if (!(stream >> lengthText))
throw std::runtime_error ("Missing payload length in frame definition."); throw std::runtime_error("Missing payload length in frame definition.");
frame.dlc = static_cast<std::uint8_t> (ParseDecimal ( frame.dlc = static_cast<std::uint8_t>(ParseDecimal(
lengthText, std::numeric_limits<std::uint8_t>::max(), "payload length")); lengthText, std::numeric_limits<std::uint8_t>::max(), "payload length"));
if (!(stream >> frame.transmitter)) if (!(stream >> frame.transmitter))
throw std::runtime_error ("Missing transmitter in frame definition."); throw std::runtime_error("Missing transmitter in frame definition.");
if (stream >> extra) if (stream >> extra)
throw std::runtime_error ("Unexpected trailing field in frame definition: " + extra); throw std::runtime_error("Unexpected trailing field in frame definition: " + extra);
NormalizeCanId (rawCanId, frame.canId, frame.isExtended); NormalizeCanId(rawCanId, frame.canId, frame.isExtended);
frame.pgn = TryExtractPgn (frame.canId, frame.isExtended, frame.hasPgn); frame.pgn = TryExtractPgn(frame.canId, frame.isExtended, frame.hasPgn);
return frame; return frame;
} }
SignalInfo DbcSignalHandler::ParseSignalLine (const std::string &line) { SignalInfo DbcSignalHandler::ParseSignalLine(const std::string &line) {
// Match the complete statement before converting any numeric fields. // Match the complete statement before converting any numeric fields.
static const std::regex syntax ( static const std::regex syntax(
R"dbc(^SG_[ \t]+([^:]+):[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*"([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc"); R"dbc(^SG_[ \t]+([^:]+):[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*"([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc");
std::smatch fields; std::smatch fields;
if (!std::regex_match (line, fields, syntax)) if (!std::regex_match(line, fields, syntax))
throw std::runtime_error ("Malformed or unsupported SG_ signal definition."); throw std::runtime_error("Malformed or unsupported SG_ signal definition.");
SignalInfo signal; SignalInfo signal;
signal.name = TrimText (fields[1].str()); signal.name = TrimText(fields[1].str());
if (signal.name.empty() || signal.name.find_first_of (" \t\r\n") != std::string::npos) if (signal.name.empty() || signal.name.find_first_of(" \t\r\n") != std::string::npos)
throw std::runtime_error ("Invalid signal name or unsupported multiplexing syntax."); throw std::runtime_error("Invalid signal name or unsupported multiplexing syntax.");
signal.startBit = ParseDecimal (fields[2].str(), signal.startBit = ParseDecimal(fields[2].str(),
std::numeric_limits<std::uint32_t>::max(), "signal start bit"); std::numeric_limits<std::uint32_t>::max(), "signal start bit");
signal.length = ParseDecimal (fields[3].str(), 64U, "signal length"); signal.length = ParseDecimal(fields[3].str(), 64U, "signal length");
if (signal.length == 0U) if (signal.length == 0U)
throw std::runtime_error ("Signal length must be within 1..64 bits."); throw std::runtime_error("Signal length must be within 1..64 bits.");
signal.isLittleEndian = fields[4].str() == "1"; signal.isLittleEndian = fields[4].str() == "1";
signal.isSigned = fields[5].str() == "-"; signal.isSigned = fields[5].str() == "-";
const auto parsePhysical = [] (const std::string &text, const char *field) { const auto parsePhysical = [](const std::string &text, const char *field) {
static const std::regex number ( static const std::regex number(
R"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)"); R"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
if (!std::regex_match (text, number)) if (!std::regex_match(text, number))
throw std::runtime_error (std::string ("Invalid ") + field + ": " + text); throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
std::size_t consumed = 0U; std::size_t consumed = 0U;
double value; double value;
try { try {
value = std::stod (text, &consumed); value = std::stod(text, &consumed);
} catch (const std::exception &) { } catch (const std::exception &) {
throw std::runtime_error (std::string ("Invalid ") + field + ": " + text); throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
} }
if (consumed != text.size() || !std::isfinite (value)) if (consumed != text.size() || !std::isfinite(value))
throw std::runtime_error (std::string ("Invalid ") + field + ": " + text); throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
return value; return value;
}; };
signal.factor = parsePhysical (fields[6].str(), "signal factor"); signal.factor = parsePhysical(fields[6].str(), "signal factor");
signal.offset = parsePhysical (fields[7].str(), "signal offset"); signal.offset = parsePhysical(fields[7].str(), "signal offset");
signal.minimum = parsePhysical (fields[8].str(), "signal minimum"); signal.minimum = parsePhysical(fields[8].str(), "signal minimum");
signal.maximum = parsePhysical (fields[9].str(), "signal maximum"); signal.maximum = parsePhysical(fields[9].str(), "signal maximum");
if (signal.minimum > signal.maximum) if (signal.minimum > signal.maximum)
throw std::runtime_error ("Signal minimum exceeds maximum."); throw std::runtime_error("Signal minimum exceeds maximum.");
signal.unit = fields[10].str(); signal.unit = fields[10].str();
signal.receivers = SplitReceivers (fields[11].str()); signal.receivers = SplitReceivers(fields[11].str());
return signal; return signal;
} }
void DbcCommentHandler::ParseCommentLine (const std::string &line, DbcDatabase &database) { void DbcCommentHandler::ParseCommentLine(const std::string &line, DbcDatabase &database) {
/* /*
* Examples: * Examples:
* CM_ BO_ 256 "Frame comment"; * CM_ BO_ 256 "Frame comment";
* CM_ SG_ 256 EngineSpeed "Signal comment"; * CM_ SG_ 256 EngineSpeed "Signal comment";
*/ */
std::istringstream stream (line); std::istringstream stream(line);
std::string token; std::string token;
stream >> token; stream >> token;
@@ -213,19 +213,19 @@ void DbcCommentHandler::ParseCommentLine (const std::string &line, DbcDatabase &
bool isExtended = false; bool isExtended = false;
stream >> rawCanId; stream >> rawCanId;
NormalizeCanId (rawCanId, canId, isExtended); NormalizeCanId(rawCanId, canId, isExtended);
const std::string::size_type quoteBegin = line.find ('"'); const std::string::size_type quoteBegin = line.find('"');
const std::string::size_type quoteEnd = line.rfind ('"'); const std::string::size_type quoteEnd = line.rfind('"');
if ((quoteBegin == std::string::npos) || if ((quoteBegin == std::string::npos) ||
(quoteEnd == std::string::npos) || (quoteEnd == std::string::npos) ||
(quoteEnd <= quoteBegin)) (quoteEnd <= quoteBegin))
return; return;
FrameInfo *frame = FindFrameById (database, canId, isExtended); FrameInfo *frame = FindFrameById(database, canId, isExtended);
if (frame != nullptr) if (frame != nullptr)
frame->comment = line.substr (quoteBegin + 1U, quoteEnd - quoteBegin - 1U); frame->comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
} else if (token == "SG_") { } else if (token == "SG_") {
std::uint32_t rawCanId = 0U; std::uint32_t rawCanId = 0U;
std::uint32_t canId = 0U; std::uint32_t canId = 0U;
@@ -235,27 +235,27 @@ void DbcCommentHandler::ParseCommentLine (const std::string &line, DbcDatabase &
stream >> rawCanId; stream >> rawCanId;
stream >> signalName; stream >> signalName;
NormalizeCanId (rawCanId, canId, isExtended); NormalizeCanId(rawCanId, canId, isExtended);
const std::string::size_type quoteBegin = line.find ('"'); const std::string::size_type quoteBegin = line.find('"');
const std::string::size_type quoteEnd = line.rfind ('"'); const std::string::size_type quoteEnd = line.rfind('"');
if ((quoteBegin == std::string::npos) || if ((quoteBegin == std::string::npos) ||
(quoteEnd == std::string::npos) || (quoteEnd == std::string::npos) ||
(quoteEnd <= quoteBegin)) (quoteEnd <= quoteBegin))
return; return;
FrameInfo *frame = FindFrameById (database, canId, isExtended); FrameInfo *frame = FindFrameById(database, canId, isExtended);
if (frame != nullptr) { if (frame != nullptr) {
SignalInfo *signal = FindSignalByName (*frame, signalName); SignalInfo *signal = FindSignalByName(*frame, signalName);
if (signal != nullptr) if (signal != nullptr)
signal->comment = line.substr (quoteBegin + 1U, quoteEnd - quoteBegin - 1U); signal->comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
} }
} }
} }
namespace { namespace {
FrameInfo *FindFrameById (DbcDatabase &database, FrameInfo *FindFrameById(DbcDatabase &database,
std::uint32_t canId, std::uint32_t canId,
bool isExtended) { bool isExtended) {
for (std::size_t index = 0U; index < database.frames.size(); ++index) { for (std::size_t index = 0U; index < database.frames.size(); ++index) {
@@ -265,59 +265,59 @@ FrameInfo *FindFrameById (DbcDatabase &database,
} }
return nullptr; return nullptr;
} }
SignalInfo *FindSignalByName (FrameInfo &frame, const std::string &signalName) { SignalInfo *FindSignalByName(FrameInfo &frame, const std::string &signalName) {
for (std::size_t index = 0U; index < frame.signals.size(); ++index) { for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
if (frame.signals[index].name == signalName) if (frame.signals[index].name == signalName)
return &frame.signals[index]; return &frame.signals[index];
} }
return nullptr; return nullptr;
} }
} }// namespace
void DbcFrameHandler::Handle (const std::string &line, DbcParseContext &context) const { void DbcFrameHandler::Handle(const std::string &line, DbcParseContext &context) const {
context.database.frames.push_back (ParseFrameLine (line)); context.database.frames.push_back(ParseFrameLine(line));
context.currentFrame = &context.database.frames.back(); context.currentFrame = &context.database.frames.back();
} }
void DbcSignalHandler::Handle (const std::string &line, DbcParseContext &context) const { void DbcSignalHandler::Handle(const std::string &line, DbcParseContext &context) const {
if (context.currentFrame == nullptr) if (context.currentFrame == nullptr)
throw std::runtime_error ("Signal found before any frame definition."); throw std::runtime_error("Signal found before any frame definition.");
SignalInfo signal = ParseSignalLine (line); SignalInfo signal = ParseSignalLine(line);
const std::uint64_t payloadBits = static_cast<std::uint64_t> (context.currentFrame->dlc) * 8U; const std::uint64_t payloadBits = static_cast<std::uint64_t>(context.currentFrame->dlc) * 8U;
std::uint64_t bit = signal.startBit; std::uint64_t bit = signal.startBit;
for (std::uint32_t index = 0U; index < signal.length; ++index) { for (std::uint32_t index = 0U; index < signal.length; ++index) {
if (bit >= payloadBits) if (bit >= payloadBits)
throw std::runtime_error ("Signal exceeds the frame payload length."); throw std::runtime_error("Signal exceeds the frame payload length.");
if (signal.isLittleEndian) if (signal.isLittleEndian)
++bit; ++bit;
else else
bit = (bit % 8U == 0U) ? bit + 15U : bit - 1U; bit = (bit % 8U == 0U) ? bit + 15U : bit - 1U;
} }
context.currentFrame->signals.push_back (std::move (signal)); context.currentFrame->signals.push_back(std::move(signal));
} }
void DbcCommentHandler::Handle (const std::string &line, DbcParseContext &context) const { void DbcCommentHandler::Handle(const std::string &line, DbcParseContext &context) const {
if (line.compare (0U, 4U, "CM_ ") == 0) if (line.compare(0U, 4U, "CM_ ") == 0)
ParseCommentLine (line, context.database); ParseCommentLine(line, context.database);
} }
DbcStatementDispatcher::DbcStatementDispatcher() { DbcStatementDispatcher::DbcStatementDispatcher() {
RegisterType<DbcFrameHandler> ("BO_"); RegisterType<DbcFrameHandler>("BO_");
RegisterType<DbcSignalHandler> ("SG_"); RegisterType<DbcSignalHandler>("SG_");
RegisterType<DbcCommentHandler> ("CM_"); RegisterType<DbcCommentHandler>("CM_");
} }
bool DbcStatementDispatcher::Dispatch (const std::string &line, bool DbcStatementDispatcher::Dispatch(const std::string &line,
DbcParseContext &context) const { DbcParseContext &context) const {
std::istringstream stream (line); std::istringstream stream(line);
std::string keyword; std::string keyword;
stream >> keyword; stream >> keyword;
const auto handler = m_handlers.find (keyword); const auto handler = m_handlers.find(keyword);
if (handler == m_handlers.end()) if (handler == m_handlers.end())
return false; return false;
handler->second->Handle (line, context); handler->second->Handle(line, context);
return true; return true;
} }
+25 -22
View File
@@ -14,49 +14,52 @@ struct DbcParseContext {
}; };
class DbcStatementHandler { class DbcStatementHandler {
public: public:
virtual ~DbcStatementHandler() = default; virtual ~DbcStatementHandler() = default;
virtual void Handle (const std::string &line, DbcParseContext &context) const = 0; virtual void Handle(const std::string &line, DbcParseContext &context) const = 0;
}; };
class DbcFrameHandler : public DbcStatementHandler { class DbcFrameHandler : public DbcStatementHandler {
public: public:
void Handle (const std::string &line, DbcParseContext &context) const override; void Handle(const std::string &line, DbcParseContext &context) const override;
private:
static FrameInfo ParseFrameLine (const std::string &line); private:
static FrameInfo ParseFrameLine(const std::string &line);
}; };
class DbcSignalHandler : public DbcStatementHandler { class DbcSignalHandler : public DbcStatementHandler {
public: public:
void Handle (const std::string &line, DbcParseContext &context) const override; void Handle(const std::string &line, DbcParseContext &context) const override;
private:
static SignalInfo ParseSignalLine (const std::string &line); private:
static SignalInfo ParseSignalLine(const std::string &line);
}; };
class DbcCommentHandler : public DbcStatementHandler { class DbcCommentHandler : public DbcStatementHandler {
public: public:
void Handle (const std::string &line, DbcParseContext &context) const override; void Handle(const std::string &line, DbcParseContext &context) const override;
private:
static void ParseCommentLine (const std::string &line, DbcDatabase &database); private:
static void ParseCommentLine(const std::string &line, DbcDatabase &database);
}; };
/** Exact-key dispatch to handlers created once per registration via TypeFactory. */ /** Exact-key dispatch to handlers created once per registration via TypeFactory. */
class DbcStatementDispatcher { class DbcStatementDispatcher {
public: public:
DbcStatementDispatcher(); DbcStatementDispatcher();
template <class Handler> template<class Handler>
void RegisterType (const std::string &keyword) { void RegisterType(const std::string &keyword) {
m_factory.registerType<Handler> (keyword); m_factory.registerType<Handler>(keyword);
m_handlers.insert_or_assign (keyword, m_factory.create (keyword)); m_handlers.insert_or_assign(keyword, m_factory.create(keyword));
} }
/** Returns false for an unregistered keyword; input must be trimmed. */ /** Returns false for an unregistered keyword; input must be trimmed. */
bool Dispatch (const std::string &line, DbcParseContext &context) const; bool Dispatch(const std::string &line, DbcParseContext &context) const;
private: private:
TypeFactory<std::string, DbcStatementHandler> m_factory; TypeFactory<std::string, DbcStatementHandler> m_factory;
std::unordered_map<std::string, std::shared_ptr<DbcStatementHandler> > m_handlers; std::unordered_map<std::string, std::shared_ptr<DbcStatementHandler>> m_handlers;
}; };
#endif /* DBC_STATEMENT_HANDLER_H */ #endif /* DBC_STATEMENT_HANDLER_H */
+6 -6
View File
@@ -11,20 +11,20 @@
#include "dbc_tree_builder.h" #include "dbc_tree_builder.h"
std::unique_ptr<TreeNode> DbcTreeBuilder::Build (const DbcDatabase &database) const { std::unique_ptr<TreeNode> DbcTreeBuilder::Build(const DbcDatabase &database) const {
std::unique_ptr<TreeNode> root (new TreeNode()); std::unique_ptr<TreeNode> root(new TreeNode());
for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) { for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) {
const FrameInfo &frame = database.frames[frameIndex]; const FrameInfo &frame = database.frames[frameIndex];
std::unique_ptr<TreeNode> frameNode (new TreeNode (frame)); std::unique_ptr<TreeNode> frameNode(new TreeNode(frame));
for (std::size_t signalIndex = 0U; signalIndex < frame.signals.size(); ++signalIndex) { for (std::size_t signalIndex = 0U; signalIndex < frame.signals.size(); ++signalIndex) {
const SignalInfo &signal = frame.signals[signalIndex]; const SignalInfo &signal = frame.signals[signalIndex];
std::unique_ptr<TreeNode> signalNode (new TreeNode (signal)); std::unique_ptr<TreeNode> signalNode(new TreeNode(signal));
frameNode->AddChild (std::move (signalNode)); frameNode->AddChild(std::move(signalNode));
} }
root->AddChild (std::move (frameNode)); root->AddChild(std::move(frameNode));
} }
return root; return root;
+2 -2
View File
@@ -21,13 +21,13 @@
* @brief Builds a simple tree from parsed DBC database. * @brief Builds a simple tree from parsed DBC database.
*/ */
class DbcTreeBuilder { class DbcTreeBuilder {
public: public:
/** /**
* @brief Build tree representation of parsed DBC data. * @brief Build tree representation of parsed DBC data.
* @param database Parsed database. * @param database Parsed database.
* @return Root node of the tree. * @return Root node of the tree.
*/ */
std::unique_ptr<TreeNode> Build (const DbcDatabase &database) const; std::unique_ptr<TreeNode> Build(const DbcDatabase &database) const;
}; };
#endif /* DBC_TREE_BUILDER_H */ #endif /* DBC_TREE_BUILDER_H */
+11 -32
View File
@@ -12,10 +12,10 @@
#ifndef DECODE_DATABASE_H #ifndef DECODE_DATABASE_H
#define DECODE_DATABASE_H #define DECODE_DATABASE_H
#include <string>
#include <vector>
#include <unordered_map>
#include <cstdint> #include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>
/** /**
* @brief Signal byte order used for runtime decoding. * @brief Signal byte order used for runtime decoding.
@@ -51,18 +51,7 @@ struct DecodeSignal {
std::string comment; /**< Comment. */ std::string comment; /**< Comment. */
DecodeSignal() DecodeSignal()
: name() : name(), startBit(0U), length(0U), byteOrder(ByteOrder::Intel), valueType(ValueType::Unsigned), factor(1.0), offset(0.0), minimum(0.0), maximum(0.0), unit(), receivers(), comment() {
, startBit (0U)
, length (0U)
, byteOrder (ByteOrder::Intel)
, valueType (ValueType::Unsigned)
, factor (1.0)
, offset (0.0)
, minimum (0.0)
, maximum (0.0)
, unit()
, receivers()
, comment() {
} }
}; };
@@ -81,15 +70,7 @@ struct DecodeFrame {
std::vector<DecodeSignal> signals; /**< Signal definitions. */ std::vector<DecodeSignal> signals; /**< Signal definitions. */
DecodeFrame() DecodeFrame()
: name() : name(), canId(0U), isExtended(false), dlc(0U), pgn(0U), hasPgn(false), transmitter(), comment(), signals() {
, canId (0U)
, isExtended (false)
, dlc (0U)
, pgn (0U)
, hasPgn (false)
, transmitter()
, comment()
, signals() {
} }
}; };
@@ -101,16 +82,14 @@ struct FrameKey {
bool isExtended; bool isExtended;
FrameKey() FrameKey()
: canId (0U) : canId(0U), isExtended(false) {
, isExtended (false) {
} }
FrameKey (std::uint32_t id, bool extended) FrameKey(std::uint32_t id, bool extended)
: canId (id) : canId(id), isExtended(extended) {
, isExtended (extended) {
} }
bool operator== (const FrameKey &other) const { bool operator==(const FrameKey &other) const {
return (canId == other.canId) && (isExtended == other.isExtended); return (canId == other.canId) && (isExtended == other.isExtended);
} }
}; };
@@ -119,8 +98,8 @@ struct FrameKey {
* @brief Hasher for frame key. * @brief Hasher for frame key.
*/ */
struct FrameKeyHasher { struct FrameKeyHasher {
std::size_t operator() (const FrameKey &key) const { std::size_t operator()(const FrameKey &key) const {
const std::size_t a = static_cast<std::size_t> (key.canId); const std::size_t a = static_cast<std::size_t>(key.canId);
const std::size_t b = key.isExtended ? 1U : 0U; const std::size_t b = key.isExtended ? 1U : 0U;
return (a * 1315423911U) ^ b; return (a * 1315423911U) ^ b;
} }
+2 -10
View File
@@ -12,9 +12,9 @@
#ifndef FRAME_INFO_H #ifndef FRAME_INFO_H
#define FRAME_INFO_H #define FRAME_INFO_H
#include <cstdint>
#include <string> #include <string>
#include <vector> #include <vector>
#include <cstdint>
#include "signal_info.h" #include "signal_info.h"
@@ -33,15 +33,7 @@ struct FrameInfo {
std::vector<SignalInfo> signals; /**< Signals contained in the frame. */ std::vector<SignalInfo> signals; /**< Signals contained in the frame. */
FrameInfo() FrameInfo()
: name() : name(), canId(0U), isExtended(false), pgn(0U), hasPgn(false), dlc(0U), transmitter(), comment(), signals() {
, canId (0U)
, isExtended (false)
, pgn (0U)
, hasPgn (false)
, dlc (0U)
, transmitter()
, comment()
, signals() {
} }
}; };
+17 -17
View File
@@ -9,18 +9,18 @@
* See LICENSE file in the project root for full license text. * See LICENSE file in the project root for full license text.
*/ */
#include <iostream> #include <cstddef>
#include <iomanip> #include <iomanip>
#include <iostream>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <cstddef>
#include "dbc_parser.h"
#include "dbc_tree_builder.h"
#include "dbc_decode_builder.h" #include "dbc_decode_builder.h"
#include "dbc_decoder.h" #include "dbc_decoder.h"
#include "dbc_parser.h"
#include "dbc_tree_builder.h"
static void PrintTree (const TreeNode *node, int indent) { static void PrintTree(const TreeNode *node, int indent) {
if (node == nullptr) if (node == nullptr)
return; return;
@@ -39,7 +39,7 @@ static void PrintTree (const TreeNode *node, int indent) {
if (frame != nullptr) { if (frame != nullptr) {
std::cout << " id=0x" << std::hex << frame->canId << std::dec std::cout << " id=0x" << std::hex << frame->canId << std::dec
<< " ext=" << (frame->isExtended ? "yes" : "no") << " ext=" << (frame->isExtended ? "yes" : "no")
<< " dlc=" << static_cast<unsigned int> (frame->dlc); << " dlc=" << static_cast<unsigned int>(frame->dlc);
if (frame->hasPgn) if (frame->hasPgn)
std::cout << " pgn=" << frame->pgn; std::cout << " pgn=" << frame->pgn;
@@ -69,10 +69,10 @@ static void PrintTree (const TreeNode *node, int indent) {
} }
for (std::size_t i = 0U; i < node->GetChildCount(); ++i) for (std::size_t i = 0U; i < node->GetChildCount(); ++i)
PrintTree (node->GetChild (i), indent + 1); PrintTree(node->GetChild(i), indent + 1);
} }
static void PrintDecodedFrame (const DecodedFrameValue &decoded) { static void PrintDecodedFrame(const DecodedFrameValue &decoded) {
if (!decoded.valid || (decoded.definition == nullptr)) { if (!decoded.valid || (decoded.definition == nullptr)) {
std::cout << "No frame definition found.\n"; std::cout << "No frame definition found.\n";
return; return;
@@ -88,14 +88,14 @@ static void PrintDecodedFrame (const DecodedFrameValue &decoded) {
std::cout << " " << signal.definition->name std::cout << " " << signal.definition->name
<< " raw="; << " raw=";
std::visit ([] (auto rawValue) { std::cout << rawValue; }, signal.rawValue); std::visit([](auto rawValue) { std::cout << rawValue; }, signal.rawValue);
std::cout << " physical=" << signal.physicalValue std::cout << " physical=" << signal.physicalValue
<< " " << signal.definition->unit << " " << signal.definition->unit
<< "\n"; << "\n";
} }
} }
int main (int argc, char *argv[]) { int main(int argc, char *argv[]) {
if (argc < 2) { if (argc < 2) {
std::cerr << "Usage: dbc_demo <file.dbc>\n"; std::cerr << "Usage: dbc_demo <file.dbc>\n";
return 1; return 1;
@@ -103,16 +103,16 @@ int main (int argc, char *argv[]) {
try { try {
DbcParser parser; DbcParser parser;
DbcDatabase database = parser.ParseFile (argv[1]); DbcDatabase database = parser.ParseFile(argv[1]);
DbcTreeBuilder treeBuilder; DbcTreeBuilder treeBuilder;
std::unique_ptr<TreeNode> root = treeBuilder.Build (database); std::unique_ptr<TreeNode> root = treeBuilder.Build(database);
std::cout << "=== Parsed tree ===\n"; std::cout << "=== Parsed tree ===\n";
PrintTree (root.get(), 0); PrintTree(root.get(), 0);
DbcDecodeBuilder decodeBuilder; DbcDecodeBuilder decodeBuilder;
DecodeDatabase decodeDatabase = decodeBuilder.Build (database); DecodeDatabase decodeDatabase = decodeBuilder.Build(database);
/* /*
* Example raw frame. * Example raw frame.
@@ -121,13 +121,13 @@ int main (int argc, char *argv[]) {
RawCanFrame rawFrame; RawCanFrame rawFrame;
rawFrame.canId = decodeDatabase.frames.empty() ? 0U : decodeDatabase.frames[0].canId; rawFrame.canId = decodeDatabase.frames.empty() ? 0U : decodeDatabase.frames[0].canId;
rawFrame.isExtended = decodeDatabase.frames.empty() ? false : decodeDatabase.frames[0].isExtended; rawFrame.isExtended = decodeDatabase.frames.empty() ? false : decodeDatabase.frames[0].isExtended;
rawFrame.data.resize (8U, 0U); rawFrame.data.resize(8U, 0U);
DbcDecoder decoder; DbcDecoder decoder;
DecodedFrameValue decoded = decoder.Decode (decodeDatabase, rawFrame); DecodedFrameValue decoded = decoder.Decode(decodeDatabase, rawFrame);
std::cout << "\n=== Decoded frame ===\n"; std::cout << "\n=== Decoded frame ===\n";
PrintDecodedFrame (decoded); PrintDecodedFrame(decoded);
} catch (const std::exception &ex) { } catch (const std::exception &ex) {
std::cerr << "Error: " << ex.what() << "\n"; std::cerr << "Error: " << ex.what() << "\n";
return 2; return 2;
+2 -13
View File
@@ -12,9 +12,9 @@
#ifndef SIGNAL_INFO_H #ifndef SIGNAL_INFO_H
#define SIGNAL_INFO_H #define SIGNAL_INFO_H
#include <cstdint>
#include <string> #include <string>
#include <vector> #include <vector>
#include <cstdint>
/** /**
* @brief Describes one signal inside a DBC frame. * @brief Describes one signal inside a DBC frame.
@@ -34,18 +34,7 @@ struct SignalInfo {
std::string comment; /**< Optional signal comment. */ std::string comment; /**< Optional signal comment. */
SignalInfo() SignalInfo()
: name() : name(), startBit(0U), length(0U), isLittleEndian(true), isSigned(false), factor(1.0), offset(0.0), minimum(0.0), maximum(0.0), unit(), receivers(), comment() {
, startBit (0U)
, length (0U)
, isLittleEndian (true)
, isSigned (false)
, factor (1.0)
, offset (0.0)
, minimum (0.0)
, maximum (0.0)
, unit()
, receivers()
, comment() {
} }
}; };
+49 -64
View File
@@ -20,44 +20,26 @@ struct DecodeCase {
int main() { int main() {
const DecodeCase cases[] = { const DecodeCase cases[] = {
{"Motorola reproduction", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {"Motorola reproduction", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
{0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0}, {"Motorola within byte", ByteOrder::Motorola, 5U, 4U, ValueType::Unsigned, {0xAC}, true, 11, 1.0, 0.0, 11.0},
{"Motorola within byte", ByteOrder::Motorola, 5U, 4U, ValueType::Unsigned, {"Motorola unaligned crossing", ByteOrder::Motorola, 3U, 12U, ValueType::Unsigned, {0xAB, 0xCD}, true, 0xBCD, 1.0, 0.0, 3021.0},
{0xAC}, true, 11, 1.0, 0.0, 11.0}, {"Motorola starts at bit zero", ByteOrder::Motorola, 0U, 2U, ValueType::Unsigned, {0x01, 0x80}, true, 3, 1.0, 0.0, 3.0},
{"Motorola unaligned crossing", ByteOrder::Motorola, 3U, 12U, ValueType::Unsigned, {"Motorola second byte", ByteOrder::Motorola, 15U, 16U, ValueType::Unsigned, {0xFF, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
{0xAB, 0xCD}, true, 0xBCD, 1.0, 0.0, 3021.0}, {"Motorola signed scaled", ByteOrder::Motorola, 7U, 16U, ValueType::Signed, {0xFF, 0xFE}, true, -2, 0.5, 10.0, 9.0},
{"Motorola starts at bit zero", ByteOrder::Motorola, 0U, 2U, ValueType::Unsigned, {"Motorola 64 bits", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned, {0, 0, 0, 0, 0, 0, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
{0x01, 0x80}, true, 3, 1.0, 0.0, 3.0}, {"Motorola 64 bits high bit", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned, {0x40, 0, 0, 0, 0, 0, 0, 0}, true, 0x4000000000000000LL, 1.0, 0.0, 4611686018427387904.0},
{"Motorola second byte", ByteOrder::Motorola, 15U, 16U, ValueType::Unsigned, {"Motorola short payload", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0},
{0xFF, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0}, {"Motorola empty payload", ByteOrder::Motorola, 7U, 8U, ValueType::Unsigned, {}, false, 0, 1.0, 0.0, 0.0},
{"Motorola signed scaled", ByteOrder::Motorola, 7U, 16U, ValueType::Signed, {"Motorola large start", ByteOrder::Motorola, std::numeric_limits<std::uint32_t>::max(), 8U, ValueType::Unsigned, {0xFF}, false, 0, 1.0, 0.0, 0.0},
{0xFF, 0xFE}, true, -2, 0.5, 10.0, 9.0}, {"Motorola zero length", ByteOrder::Motorola, 7U, 0U, ValueType::Unsigned, {0xFF}, false, 0, 1.0, 0.0, 0.0},
{"Motorola 64 bits", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned,
{0, 0, 0, 0, 0, 0, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
{"Motorola 64 bits high bit", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned,
{0x40, 0, 0, 0, 0, 0, 0, 0}, true, 0x4000000000000000LL,
1.0, 0.0, 4611686018427387904.0},
{"Motorola short payload", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned,
{0x12}, false, 0, 1.0, 0.0, 0.0},
{"Motorola empty payload", ByteOrder::Motorola, 7U, 8U, ValueType::Unsigned,
{}, false, 0, 1.0, 0.0, 0.0},
{"Motorola large start", ByteOrder::Motorola,
std::numeric_limits<std::uint32_t>::max(), 8U, ValueType::Unsigned,
{0xFF}, false, 0, 1.0, 0.0, 0.0},
{"Motorola zero length", ByteOrder::Motorola, 7U, 0U, ValueType::Unsigned,
{0xFF}, false, 0, 1.0, 0.0, 0.0},
{"Motorola excessive length", ByteOrder::Motorola, 7U, 65U, ValueType::Unsigned, {"Motorola excessive length", ByteOrder::Motorola, 7U, 65U, ValueType::Unsigned,
std::vector<std::uint8_t> (9U, 0xFF), false, 0, 1.0, 0.0, 0.0}, std::vector<std::uint8_t>(9U, 0xFF), false, 0, 1.0, 0.0, 0.0},
{"Intel regression", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {"Intel regression", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x3412, 1.0, 0.0, 13330.0},
{0x12, 0x34}, true, 0x3412, 1.0, 0.0, 13330.0}, {"Intel signed regression", ByteOrder::Intel, 0U, 16U, ValueType::Signed, {0xFE, 0xFF}, true, -2, 0.5, 10.0, 9.0},
{"Intel signed regression", ByteOrder::Intel, 0U, 16U, ValueType::Signed, {"Intel short payload", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0}};
{0xFE, 0xFF}, true, -2, 0.5, 10.0, 9.0},
{"Intel short payload", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned,
{0x12}, false, 0, 1.0, 0.0, 0.0}
};
int failures = 0; int failures = 0;
for (const DecodeCase &test : cases) { for (const DecodeCase &test: cases) {
DecodeSignal signal; DecodeSignal signal;
signal.name = test.name; signal.name = test.name;
signal.byteOrder = test.byteOrder; signal.byteOrder = test.byteOrder;
@@ -69,27 +51,27 @@ int main() {
DecodeFrame definition; DecodeFrame definition;
definition.canId = 0x123U; definition.canId = 0x123U;
definition.dlc = static_cast<std::uint8_t> (test.data.size()); definition.dlc = static_cast<std::uint8_t>(test.data.size());
definition.signals.push_back (signal); definition.signals.push_back(signal);
DecodeDatabase database; DecodeDatabase database;
database.frames.push_back (definition); database.frames.push_back(definition);
database.frameIndexByKey.emplace (FrameKey (definition.canId, false), 0U); database.frameIndexByKey.emplace(FrameKey(definition.canId, false), 0U);
RawCanFrame frame; RawCanFrame frame;
frame.canId = definition.canId; frame.canId = definition.canId;
frame.data = test.data; frame.data = test.data;
const DecodedFrameValue result = DbcDecoder().Decode (database, frame); const DecodedFrameValue result = DbcDecoder().Decode(database, frame);
const bool passed = result.valid && const bool passed = result.valid &&
(result.definition == &database.frames[0]) && (result.definition == &database.frames[0]) &&
(result.signals.size() == 1U) && (result.signals.size() == 1U) &&
(result.signals[0].definition == &database.frames[0].signals[0]) && (result.signals[0].definition == &database.frames[0].signals[0]) &&
(result.signals[0].valid == test.valid) && (result.signals[0].valid == test.valid) &&
(!test.valid || (((test.valueType == ValueType::Signed) (!test.valid || (((test.valueType == ValueType::Signed)
? (std::get_if<std::int64_t> (&result.signals[0].rawValue) && ? (std::get_if<std::int64_t>(&result.signals[0].rawValue) &&
std::get<std::int64_t> (result.signals[0].rawValue) == test.rawValue) std::get<std::int64_t>(result.signals[0].rawValue) == test.rawValue)
: (std::get_if<std::uint64_t> (&result.signals[0].rawValue) && : (std::get_if<std::uint64_t>(&result.signals[0].rawValue) &&
std::get<std::uint64_t> (result.signals[0].rawValue) == std::get<std::uint64_t>(result.signals[0].rawValue) ==
static_cast<std::uint64_t> (test.rawValue))) && static_cast<std::uint64_t>(test.rawValue))) &&
(result.signals[0].physicalValue == test.physicalValue))); (result.signals[0].physicalValue == test.physicalValue)));
if (!passed) { if (!passed) {
std::cerr << "FAIL: " << test.name << '\n'; std::cerr << "FAIL: " << test.name << '\n';
@@ -97,12 +79,12 @@ int main() {
} }
} }
std::size_t boundaryCases = 0U; std::size_t boundaryCases = 0U;
for (ByteOrder order : {ByteOrder::Intel, ByteOrder::Motorola}) { for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
for (ValueType type : {ValueType::Unsigned, ValueType::Signed}) { for (ValueType type: {ValueType::Unsigned, ValueType::Signed}) {
for (std::uint64_t bits : {std::uint64_t (0U), std::uint64_t (0x7FFFFFFFFFFFFFFFULL), for (std::uint64_t bits: {std::uint64_t(0U), std::uint64_t(0x7FFFFFFFFFFFFFFFULL),
std::uint64_t (0x8000000000000000ULL), std::uint64_t(0x8000000000000000ULL),
std::uint64_t (0x8000000000000001ULL), std::uint64_t(0x8000000000000001ULL),
std::uint64_t (0xFFFFFFFFFFFFFFFFULL)}) { std::uint64_t(0xFFFFFFFFFFFFFFFFULL)}) {
DecodeDatabase database; DecodeDatabase database;
DecodeFrame definition; DecodeFrame definition;
DecodeSignal signal; DecodeSignal signal;
@@ -112,28 +94,31 @@ int main() {
signal.valueType = type; signal.valueType = type;
signal.factor = 0.5; signal.factor = 0.5;
signal.offset = 10.0; signal.offset = 10.0;
definition.signals.push_back (signal); definition.signals.push_back(signal);
database.frames.push_back (definition); database.frames.push_back(definition);
database.frameIndexByKey.emplace (FrameKey (0U, false), 0U); database.frameIndexByKey.emplace(FrameKey(0U, false), 0U);
RawCanFrame frame; RawCanFrame frame;
for (unsigned byte = 0U; byte < 8U; ++byte) { for (unsigned byte = 0U; byte < 8U; ++byte) {
const unsigned shift = (order == ByteOrder::Intel ? byte : 7U - byte) * 8U; const unsigned shift = (order == ByteOrder::Intel ? byte : 7U - byte) * 8U;
frame.data.push_back (static_cast<std::uint8_t> (bits >> shift)); frame.data.push_back(static_cast<std::uint8_t>(bits >> shift));
} }
const auto result = DbcDecoder().Decode (database, frame); const auto result = DbcDecoder().Decode(database, frame);
const std::int64_t signedExpected = bits <= 0x7FFFFFFFFFFFFFFFULL const std::int64_t signedExpected = bits <= 0x7FFFFFFFFFFFFFFFULL
? static_cast<std::int64_t> (bits) ? static_cast<std::int64_t>(bits)
: std::numeric_limits<std::int64_t>::min() + : std::numeric_limits<std::int64_t>::min() +
static_cast<std::int64_t> (bits - 0x8000000000000000ULL); static_cast<std::int64_t>(bits - 0x8000000000000000ULL);
const auto &decoded = result.signals.at (0U); const auto &decoded = result.signals.at(0U);
const auto expected = type == ValueType::Unsigned const auto expected = type == ValueType::Unsigned
? std::variant<std::int64_t, std::uint64_t> (bits) ? std::variant<std::int64_t, std::uint64_t>(bits)
: std::variant<std::int64_t, std::uint64_t> (signedExpected); : std::variant<std::int64_t, std::uint64_t>(signedExpected);
const double physical = (type == ValueType::Unsigned const double physical = (type == ValueType::Unsigned
? static_cast<double> (bits) : static_cast<double> (signedExpected)) * 0.5 + 10.0; ? static_cast<double>(bits)
: static_cast<double>(signedExpected)) *
0.5 +
10.0;
std::ostringstream actualText; std::ostringstream actualText;
std::ostringstream expectedText; std::ostringstream expectedText;
std::visit ([&actualText] (auto raw) { actualText << raw; }, decoded.rawValue); std::visit([&actualText](auto raw) { actualText << raw; }, decoded.rawValue);
if (type == ValueType::Unsigned) if (type == ValueType::Unsigned)
expectedText << bits; expectedText << bits;
else else
@@ -149,6 +134,6 @@ int main() {
} }
if (failures != 0) if (failures != 0)
return 1; return 1;
std::cout << "All " << sizeof (cases) / sizeof (cases[0]) + boundaryCases << " decoder cases passed.\n"; std::cout << "All " << sizeof(cases) / sizeof(cases[0]) + boundaryCases << " decoder cases passed.\n";
return 0; return 0;
} }
+136 -113
View File
@@ -9,23 +9,25 @@
struct Fixture { struct Fixture {
std::filesystem::path path = std::filesystem::temp_directory_path() / std::filesystem::path path = std::filesystem::temp_directory_path() /
("dbc_parser_test_" + std::to_string ( ("dbc_parser_test_" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()) + ".dbc");
std::chrono::steady_clock::now().time_since_epoch().count()) + ".dbc"); ~Fixture() {
~Fixture() { std::error_code error; std::filesystem::remove (path, error); } std::error_code error;
void Write (const std::string &text) const { std::filesystem::remove(path, error);
std::ofstream output (path); }
void Write(const std::string &text) const {
std::ofstream output(path);
output << text; output << text;
if (!output) if (!output)
throw std::runtime_error ("Cannot write test fixture."); throw std::runtime_error("Cannot write test fixture.");
} }
}; };
class CountingHandler : public DbcStatementHandler { class CountingHandler : public DbcStatementHandler {
public: public:
inline static int instances = 0; inline static int instances = 0;
inline static int calls = 0; inline static int calls = 0;
CountingHandler() { ++instances; } CountingHandler() { ++instances; }
void Handle (const std::string &, DbcParseContext &context) const override { void Handle(const std::string &, DbcParseContext &context) const override {
++calls; ++calls;
context.database.frames.emplace_back(); context.database.frames.emplace_back();
} }
@@ -35,11 +37,19 @@ int main() {
Fixture fixture; Fixture fixture;
int failures = 0; int failures = 0;
std::size_t count = 0U; std::size_t count = 0U;
const auto check = [&] (bool passed, const std::string &name) { const auto check = [&](bool passed, const std::string &name) {
++count; ++count;
if (!passed) { ++failures; std::cerr << "FAIL: " << name << '\n'; } if (!passed) {
++failures;
std::cerr << "FAIL: " << name << '\n';
}
};
struct ValidCase {
const char *line;
std::uint32_t id;
bool extended;
unsigned length;
}; };
struct ValidCase { const char *line; std::uint32_t id; bool extended; unsigned length; };
const ValidCase valid[] = { const ValidCase valid[] = {
{"BO_ 0 Frame: 0 ECU", 0U, false, 0U}, {"BO_ 0 Frame: 0 ECU", 0U, false, 0U},
{"BO_ 2047 Frame: 8 ECU", 2047U, false, 8U}, {"BO_ 2047 Frame: 8 ECU", 2047U, false, 8U},
@@ -47,18 +57,18 @@ int main() {
{"BO_ 536870911 Frame: 255 ECU", 0x1FFFFFFFU, true, 255U}, {"BO_ 536870911 Frame: 255 ECU", 0x1FFFFFFFU, true, 255U},
{"BO_ 2147483649 Frame: 8 ECU", 1U, true, 8U}, {"BO_ 2147483649 Frame: 8 ECU", 1U, true, 8U},
{"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU, true, 8U}, {"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU, true, 8U},
{" BO_\t256\tFrame :\t8\tECU \r", 256U, false, 8U} {" BO_\t256\tFrame :\t8\tECU \r", 256U, false, 8U}};
}; for (const auto &test: valid) {
for (const auto &test : valid) { fixture.Write(std::string(test.line) + '\n');
fixture.Write (std::string (test.line) + '\n');
try { try {
const auto database = DbcParser().ParseFile (fixture.path.string()); const auto database = DbcParser().ParseFile(fixture.path.string());
check (database.frames.size() == 1U && database.frames[0].name == "Frame" && check(database.frames.size() == 1U && database.frames[0].name == "Frame" &&
database.frames[0].canId == test.id && database.frames[0].canId == test.id &&
database.frames[0].isExtended == test.extended && database.frames[0].isExtended == test.extended &&
database.frames[0].dlc == test.length && database.frames[0].dlc == test.length &&
database.frames[0].transmitter == "ECU", test.line); database.frames[0].transmitter == "ECU",
} catch (const std::exception &error) { check (false, error.what()); } test.line);
} catch (const std::exception &error) { check(false, error.what()); }
} }
const char *invalid[] = { const char *invalid[] = {
"BO_ invalid", "BO_", "BO_ 1", "BO_ 1 : 8 ECU", "BO_ 1 Frame 8 ECU", "BO_ invalid", "BO_", "BO_ 1", "BO_ 1 : 8 ECU", "BO_ 1 Frame 8 ECU",
@@ -70,159 +80,172 @@ int main() {
"BO_ 2684354560 Frame: 8 ECU", "BO_ 4294967295 Frame: 8 ECU", "BO_ 2684354560 Frame: 8 ECU", "BO_ 4294967295 Frame: 8 ECU",
"BO_ 1 Frame: -1 ECU", "BO_ 1 Frame: +8 ECU", "BO_ 1 Frame: 8x ECU", "BO_ 1 Frame: -1 ECU", "BO_ 1 Frame: +8 ECU", "BO_ 1 Frame: 8x ECU",
"BO_ 1 Frame: 8.5 ECU", "BO_ 1 Frame: 256 ECU", "BO_ 1 Frame: 8.5 ECU", "BO_ 1 Frame: 256 ECU",
"BO_ 1 Frame: 4294967296 ECU", "BO_\tinvalid" "BO_ 1 Frame: 4294967296 ECU", "BO_\tinvalid"};
}; for (const auto *line: invalid) {
for (const auto *line : invalid) { fixture.Write(std::string("VERSION \"test\"\n\n") + line + '\n');
fixture.Write (std::string ("VERSION \"test\"\n\n") + line + '\n');
try { try {
DbcParser().ParseFile (fixture.path.string()); DbcParser().ParseFile(fixture.path.string());
check (false, line); check(false, line);
} catch (const std::runtime_error &error) { } catch (const std::runtime_error &error) {
const std::string prefix = fixture.path.string() + ":3: "; const std::string prefix = fixture.path.string() + ":3: ";
check (std::string (error.what()).find (prefix) == 0U && check(std::string(error.what()).find(prefix) == 0U &&
std::string (error.what()).size() > prefix.size(), line); std::string(error.what()).size() > prefix.size(),
line);
} }
} }
fixture.Write ( fixture.Write(
"BO_TX_BU_ 256 : ECU;\nBO_ 256 Frame: 8 ECU\n" "BO_TX_BU_ 256 : ECU;\nBO_ 256 Frame: 8 ECU\n"
" SG_ Speed : 0|16@1+ (0.5,1) [0|100] \"rpm\" ECU\n" " SG_ Speed : 0|16@1+ (0.5,1) [0|100] \"rpm\" ECU\n"
"CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\n" "CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\n"
"BO_ 257 Second: 8 ECU\n"); "BO_ 257 Second: 8 ECU\n");
const auto database = DbcParser().ParseFile (fixture.path.string()); const auto database = DbcParser().ParseFile(fixture.path.string());
check (database.frames.size() == 2U && database.frames[0].signals.size() == 1U && check(database.frames.size() == 2U && database.frames[0].signals.size() == 1U &&
database.frames[0].comment == "Frame comment" && database.frames[0].comment == "Frame comment" &&
database.frames[0].signals[0].comment == "Signal comment", "Integration"); database.frames[0].signals[0].comment == "Signal comment",
fixture.Write ("\nSG_ Speed : invalid\n"); "Integration");
fixture.Write("\nSG_ Speed : invalid\n");
try { try {
DbcParser().ParseFile (fixture.path.string()); DbcParser().ParseFile(fixture.path.string());
check (false, "Signal error location"); check(false, "Signal error location");
} catch (const std::runtime_error &error) { } catch (const std::runtime_error &error) {
check (std::string (error.what()).find (fixture.path.string() + ":2: ") == 0U, check(std::string(error.what()).find(fixture.path.string() + ":2: ") == 0U,
"Signal error location"); "Signal error location");
} }
const DbcParser signalParser; const DbcParser signalParser;
const auto signalLine = [] (const std::string &start, const std::string &length, const auto signalLine = [](const std::string &start, const std::string &length,
const std::string &order, const std::string &factor, const std::string &order, const std::string &factor,
const std::string &offset, const std::string &minimum, const std::string &offset, const std::string &minimum,
const std::string &maximum) { const std::string &maximum) {
return "SG_ Value : " + start + "|" + length + "@" + order + return "SG_ Value : " + start + "|" + length + "@" + order +
" (" + factor + "," + offset + ") [" + minimum + "|" + maximum + "] \"\" ECU"; " (" + factor + "," + offset + ") [" + minimum + "|" + maximum + "] \"\" ECU";
}; };
const auto rejectSignal = [&] (const std::string &line, unsigned payload = 8U) { const auto rejectSignal = [&](const std::string &line, unsigned payload = 8U) {
fixture.Write ("BO_ 1 Frame: " + std::to_string (payload) + " ECU\n\n" + line + "\n"); fixture.Write("BO_ 1 Frame: " + std::to_string(payload) + " ECU\n\n" + line + "\n");
try { try {
signalParser.ParseFile (fixture.path.string()); signalParser.ParseFile(fixture.path.string());
check (false, "Reject " + line); check(false, "Reject " + line);
} catch (const std::runtime_error &error) { } catch (const std::runtime_error &error) {
const std::string prefix = fixture.path.string() + ":3: "; const std::string prefix = fixture.path.string() + ":3: ";
check (std::string (error.what()).find (prefix) == 0U && check(std::string(error.what()).find(prefix) == 0U &&
std::string (error.what()).size() > prefix.size(), "Reject " + line); 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", for (const std::string value: {"-1", "+1", "0junk", "0x10", "1.5",
"4294967296", "999999999999999999999"}) { "4294967296", "999999999999999999999"}) {
rejectSignal (signalLine (value, "8", "1+", "1", "0", "0", "255")); rejectSignal(signalLine(value, "8", "1+", "1", "0", "0", "255"));
rejectSignal (signalLine ("0", value, "1+", "1", "0", "0", "255")); rejectSignal(signalLine("0", value, "1+", "1", "0", "0", "255"));
} }
for (const std::string length : {"0", "65"}) for (const std::string length: {"0", "65"})
rejectSignal (signalLine ("0", length, "1+", "1", "0", "0", "255")); rejectSignal(signalLine("0", length, "1+", "1", "0", "0", "255"));
for (const std::string order : {"2+", "9-", "1", "1x", "1++", "01+"}) for (const std::string order: {"2+", "9-", "1", "1x", "1++", "01+"})
rejectSignal (signalLine ("0", "8", order, "1", "0", "0", "255")); rejectSignal(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"}) { 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+", value, "0", "0", "255"));
rejectSignal (signalLine ("0", "8", "1+", "1", value, "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", value, "255"));
rejectSignal (signalLine ("0", "8", "1+", "1", "0", "0", value)); rejectSignal(signalLine("0", "8", "1+", "1", "0", "0", value));
} }
rejectSignal (signalLine ("0", "8", "1+", "1", "0", "2", "1")); rejectSignal(signalLine("0", "8", "1+", "1", "0", "2", "1"));
for (const std::string name : {"", "Two Names", "Value M", "Value m1", "Value m1M"}) { for (const std::string name: {"", "Two Names", "Value M", "Value m1", "Value m1M"}) {
std::string line = ordinary; std::string line = ordinary;
line.replace (4U, 5U, name); line.replace(4U, 5U, name);
rejectSignal (line); rejectSignal(line);
} }
for (const std::string broken : { for (const std::string broken: {
"SG_", "SG_ Value", "SG_ Value : 0|8@1+ (1,0) [0|255] ECU", "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] \"\"",
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU,", "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU,",
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU extra"}) "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU extra"})
rejectSignal (broken); rejectSignal(broken);
rejectSignal (signalLine ("64", "1", "1+", "1", "0", "0", "1")); rejectSignal(signalLine("64", "1", "1+", "1", "0", "0", "1"));
rejectSignal (signalLine ("63", "2", "1+", "1", "0", "0", "3")); rejectSignal(signalLine("63", "2", "1+", "1", "0", "0", "3"));
rejectSignal (signalLine ("56", "2", "0+", "1", "0", "0", "3")); rejectSignal(signalLine("56", "2", "0+", "1", "0", "0", "3"));
rejectSignal (signalLine ("4294967295", "64", "1+", "1", "0", "0", "1")); rejectSignal(signalLine("4294967295", "64", "1+", "1", "0", "0", "1"));
rejectSignal (ordinary, 0U); rejectSignal(ordinary, 0U);
for (const char delimiter : {'|', '@', '(', ')', '[', ']', '"'}) { for (const char delimiter: {'|', '@', '(', ')', '[', ']', '"'}) {
std::string line = ordinary; std::string line = ordinary;
line.erase (line.find (delimiter), 1U); line.erase(line.find(delimiter), 1U);
rejectSignal (line); rejectSignal(line);
} }
fixture.Write ("BO_ 1 Frame: 8 ECU\n" + fixture.Write("BO_ 1 Frame: 8 ECU\n" +
signalLine ("0", "8", "1+", "0", "0", "0", "0") + "\n" + signalLine("0", "8", "1+", "0", "0", "0", "0") + "\n" +
signalLine ("8", "8", "1-", "-1", "0", "-255", "0") + "\n"); signalLine("8", "8", "1-", "-1", "0", "-255", "0") + "\n");
const auto factors = signalParser.ParseFile (fixture.path.string()); const auto factors = signalParser.ParseFile(fixture.path.string());
check (factors.frames[0].signals.size() == 2U && check(factors.frames[0].signals.size() == 2U &&
factors.frames[0].signals[0].factor == 0.0 && factors.frames[0].signals[0].factor == 0.0 &&
factors.frames[0].signals[1].factor == -1.0, factors.frames[0].signals[1].factor == -1.0,
"Zero and negative factors and equal range boundaries"); "Zero and negative factors and equal range boundaries");
struct SignalCase { const char *start; const char *length; const char *order; unsigned payload; }; struct SignalCase {
const SignalCase validSignals[] = { const char *start;
{"0", "1", "1+", 1U}, {"7", "1", "0-", 1U}, const char *length;
{"0", "64", "1+", 8U}, {"7", "64", "0-", 8U}, const char *order;
{"63", "1", "1+", 8U}, {"56", "1", "0+", 8U}, unsigned payload;
{"3", "12", "0+", 2U}, {"2039", "1", "1+", 255U}
}; };
for (const auto &test : validSignals) { const SignalCase validSignals[] = {
fixture.Write ("BO_ 1 Frame: " + std::to_string (test.payload) + " ECU\n" + {"0", "1", "1+", 1U},
signalLine (test.start, test.length, test.order, ".5e+1", "-2.5E-1", "-1.", "+2e2") + "\n"); {"7", "1", "0-", 1U},
{"0", "64", "1+", 8U},
{"7", "64", "0-", 8U},
{"63", "1", "1+", 8U},
{"56", "1", "0+", 8U},
{"3", "12", "0+", 2U},
{"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");
try { try {
const auto parsed = signalParser.ParseFile (fixture.path.string()); const auto parsed = signalParser.ParseFile(fixture.path.string());
const auto &signal = parsed.frames.at (0).signals.at (0); const auto &signal = parsed.frames.at(0).signals.at(0);
check (signal.startBit == std::stoul (test.start) && check(signal.startBit == std::stoul(test.start) &&
signal.length == std::stoul (test.length) && signal.length == std::stoul(test.length) &&
signal.isLittleEndian == (test.order[0] == '1') && signal.isLittleEndian == (test.order[0] == '1') &&
signal.isSigned == (test.order[1] == '-') && signal.isSigned == (test.order[1] == '-') &&
signal.factor == 5.0 && signal.offset == -0.25 && signal.factor == 5.0 && signal.offset == -0.25 &&
signal.minimum == -1.0 && signal.maximum == 200.0, signal.minimum == -1.0 && signal.maximum == 200.0,
"Valid signal boundary and numeric metadata"); "Valid signal boundary and numeric metadata");
} catch (const std::exception &error) { check (false, error.what()); } } catch (const std::exception &error) { check(false, error.what()); }
} }
fixture.Write ("BO_ 1 Frame: 8 ECU\n" fixture.Write("BO_ 1 Frame: 8 ECU\n"
"SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n"); "SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n");
try { try {
const auto parsed = signalParser.ParseFile (fixture.path.string()); const auto parsed = signalParser.ParseFile(fixture.path.string());
const auto &signal = parsed.frames.at (0).signals.at (0); const auto &signal = parsed.frames.at(0).signals.at(0);
check (signal.name == "Value" && signal.unit == "rpm" && check(signal.name == "Value" && signal.unit == "rpm" &&
signal.receivers == std::vector<std::string> ({"ECU1", "ECU2"}), signal.receivers == std::vector<std::string>({"ECU1", "ECU2"}),
"Signal tabs, spaces, and receiver list"); "Signal tabs, spaces, and receiver list");
} catch (const std::exception &error) { check (false, error.what()); } } catch (const std::exception &error) { check(false, error.what()); }
DbcDatabase dispatchDatabase; DbcDatabase dispatchDatabase;
DbcParseContext context {dispatchDatabase, nullptr}; DbcParseContext context{dispatchDatabase, nullptr};
DbcStatementDispatcher dispatcher; DbcStatementDispatcher dispatcher;
dispatcher.RegisterType<CountingHandler> ("TEST_"); dispatcher.RegisterType<CountingHandler>("TEST_");
check (dispatcher.Dispatch ("TEST_ first", context) && check(dispatcher.Dispatch("TEST_ first", context) &&
dispatcher.Dispatch ("TEST_ second", context) && dispatcher.Dispatch("TEST_ second", context) &&
CountingHandler::instances == 1 && CountingHandler::calls == 2 && CountingHandler::instances == 1 && CountingHandler::calls == 2 &&
dispatchDatabase.frames.size() == 2U, "Factory dispatch and handler reuse"); dispatchDatabase.frames.size() == 2U,
check (!dispatcher.Dispatch ("TEST_OTHER data", context) && "Factory dispatch and handler reuse");
!dispatcher.Dispatch ("BO_TX_BU_ 1 : ECU;", context) && check(!dispatcher.Dispatch("TEST_OTHER data", context) &&
!dispatcher.Dispatch ("VAL_ 1 Speed 0 \"Stopped\";", context) && !dispatcher.Dispatch("BO_TX_BU_ 1 : ECU;", context) &&
CountingHandler::calls == 2, "Exact and unknown keyword dispatch"); !dispatcher.Dispatch("VAL_ 1 Speed 0 \"Stopped\";", context) &&
CountingHandler::calls == 2,
"Exact and unknown keyword dispatch");
const DbcParser parser; const DbcParser parser;
fixture.Write ("BO_ 1 First: 8 ECU\n"); fixture.Write("BO_ 1 First: 8 ECU\n");
check (parser.ParseFile (fixture.path.string()).frames.size() == 1U, "First parse"); check(parser.ParseFile(fixture.path.string()).frames.size() == 1U, "First parse");
fixture.Write ("SG_ Speed : invalid\n"); fixture.Write("SG_ Speed : invalid\n");
try { try {
parser.ParseFile (fixture.path.string()); parser.ParseFile(fixture.path.string());
check (false, "Independent parse context"); check(false, "Independent parse context");
} catch (const std::runtime_error &error) { } catch (const std::runtime_error &error) {
check (std::string (error.what()).find ("Signal found before any frame") != check(std::string(error.what()).find("Signal found before any frame") !=
std::string::npos, "Independent parse context"); std::string::npos,
"Independent parse context");
} }
fixture.Write ("BO_ 2 Second: 8 ECU\n"); fixture.Write("BO_ 2 Second: 8 ECU\n");
const auto recovered = parser.ParseFile (fixture.path.string()); const auto recovered = parser.ParseFile(fixture.path.string());
check (recovered.frames.size() == 1U && recovered.frames[0].canId == 2U, check(recovered.frames.size() == 1U && recovered.frames[0].canId == 2U,
"Fresh parse after error"); "Fresh parse after error");
if (failures != 0) return 1; if (failures != 0) return 1;
std::cout << "All " << count << " parser cases passed.\n"; std::cout << "All " << count << " parser cases passed.\n";
+9 -21
View File
@@ -12,46 +12,34 @@
#include "tree_node.h" #include "tree_node.h"
TreeNode::TreeNode() TreeNode::TreeNode()
: m_type (NodeType::Root) : m_type(NodeType::Root), m_name("dbc"), m_children(), m_frame(), m_signal() {
, m_name ("dbc")
, m_children()
, m_frame()
, m_signal() {
} }
TreeNode::TreeNode (const FrameInfo &frame) TreeNode::TreeNode(const FrameInfo &frame)
: m_type (NodeType::Frame) : m_type(NodeType::Frame), m_name(frame.name), m_children(), m_frame(new FrameInfo(frame)), m_signal() {
, m_name (frame.name)
, m_children()
, m_frame (new FrameInfo (frame))
, m_signal() {
} }
TreeNode::TreeNode (const SignalInfo &signal) TreeNode::TreeNode(const SignalInfo &signal)
: m_type (NodeType::Signal) : m_type(NodeType::Signal), m_name(signal.name), m_children(), m_frame(), m_signal(new SignalInfo(signal)) {
, m_name (signal.name)
, m_children()
, m_frame()
, m_signal (new SignalInfo (signal)) {
} }
void TreeNode::AddChild (std::unique_ptr<TreeNode> child) { void TreeNode::AddChild(std::unique_ptr<TreeNode> child) {
if (child) if (child)
m_children.push_back (std::move (child)); m_children.push_back(std::move(child));
} }
std::size_t TreeNode::GetChildCount() const { std::size_t TreeNode::GetChildCount() const {
return m_children.size(); return m_children.size();
} }
const TreeNode *TreeNode::GetChild (std::size_t index) const { const TreeNode *TreeNode::GetChild(std::size_t index) const {
if (index >= m_children.size()) if (index >= m_children.size())
return nullptr; return nullptr;
return m_children[index].get(); return m_children[index].get();
} }
TreeNode *TreeNode::GetChild (std::size_t index) { TreeNode *TreeNode::GetChild(std::size_t index) {
if (index >= m_children.size()) if (index >= m_children.size())
return nullptr; return nullptr;
+14 -14
View File
@@ -12,10 +12,10 @@
#ifndef TREE_NODE_H #ifndef TREE_NODE_H
#define TREE_NODE_H #define TREE_NODE_H
#include <cstddef>
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory>
#include <cstddef>
#include "frame_info.h" #include "frame_info.h"
#include "signal_info.h" #include "signal_info.h"
@@ -33,7 +33,7 @@ enum class NodeType {
* @brief Tree node for later use in model/view or other hierarchy consumers. * @brief Tree node for later use in model/view or other hierarchy consumers.
*/ */
class TreeNode { class TreeNode {
public: public:
/** /**
* @brief Create root node. * @brief Create root node.
*/ */
@@ -43,27 +43,27 @@ class TreeNode {
* @brief Create frame node. * @brief Create frame node.
* @param frame Frame payload. * @param frame Frame payload.
*/ */
explicit TreeNode (const FrameInfo &frame); explicit TreeNode(const FrameInfo &frame);
/** /**
* @brief Create signal node. * @brief Create signal node.
* @param signal Signal payload. * @param signal Signal payload.
*/ */
explicit TreeNode (const SignalInfo &signal); explicit TreeNode(const SignalInfo &signal);
~TreeNode() = default; ~TreeNode() = default;
TreeNode (const TreeNode &) = delete; TreeNode(const TreeNode &) = delete;
TreeNode &operator= (const TreeNode &) = delete; TreeNode &operator=(const TreeNode &) = delete;
TreeNode (TreeNode &&) = default; TreeNode(TreeNode &&) = default;
TreeNode &operator= (TreeNode &&) = default; TreeNode &operator=(TreeNode &&) = default;
/** /**
* @brief Add child node. * @brief Add child node.
* @param child Child node to add. * @param child Child node to add.
*/ */
void AddChild (std::unique_ptr<TreeNode> child); void AddChild(std::unique_ptr<TreeNode> child);
/** /**
* @brief Get child count. * @brief Get child count.
@@ -76,14 +76,14 @@ class TreeNode {
* @param index Child index. * @param index Child index.
* @return Child pointer or nullptr if index is invalid. * @return Child pointer or nullptr if index is invalid.
*/ */
const TreeNode *GetChild (std::size_t index) const; const TreeNode *GetChild(std::size_t index) const;
/** /**
* @brief Get mutable child by index. * @brief Get mutable child by index.
* @param index Child index. * @param index Child index.
* @return Child pointer or nullptr if index is invalid. * @return Child pointer or nullptr if index is invalid.
*/ */
TreeNode *GetChild (std::size_t index); TreeNode *GetChild(std::size_t index);
/** /**
* @brief Get node type. * @brief Get node type.
@@ -109,10 +109,10 @@ class TreeNode {
*/ */
const SignalInfo *GetSignal() const; const SignalInfo *GetSignal() const;
private: private:
NodeType m_type; NodeType m_type;
std::string m_name; std::string m_name;
std::vector<std::unique_ptr<TreeNode> > m_children; std::vector<std::unique_ptr<TreeNode>> m_children;
std::unique_ptr<FrameInfo> m_frame; std::unique_ptr<FrameInfo> m_frame;
std::unique_ptr<SignalInfo> m_signal; std::unique_ptr<SignalInfo> m_signal;
}; };
+9 -9
View File
@@ -7,7 +7,7 @@
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include <unordered_map> #include <unordered_map>
#include <utility> // std::move #include <utility>// std::move
/** /**
* @brief Registry-based factory for creating objects by runtime identifier. * @brief Registry-based factory for creating objects by runtime identifier.
@@ -55,7 +55,7 @@
* (void)b->get(); * (void)b->get();
* @endcode * @endcode
*/ */
template <class ClassId, class BaseClass, class... Args> template<class ClassId, class BaseClass, class... Args>
class TypeFactory { class TypeFactory {
public: public:
using BasePtr = std::shared_ptr<BaseClass>; using BasePtr = std::shared_ptr<BaseClass>;
@@ -72,8 +72,8 @@ public:
* *
* Re-registering the same id overwrites the previous entry ("last wins"). * Re-registering the same id overwrites the previous entry ("last wins").
*/ */
template <class Derived> template<class Derived>
void registerType(const ClassId& id) { void registerType(const ClassId &id) {
static_assert(std::is_base_of_v<BaseClass, Derived>, static_assert(std::is_base_of_v<BaseClass, Derived>,
"Derived must inherit from BaseClass"); "Derived must inherit from BaseClass");
static_assert(std::is_constructible_v<Derived, Args...>, static_assert(std::is_constructible_v<Derived, Args...>,
@@ -86,7 +86,7 @@ public:
* @brief Returns creator function for the given id. * @brief Returns creator function for the given id.
* @throws std::out_of_range when id is not registered. * @throws std::out_of_range when id is not registered.
*/ */
[[nodiscard]] CreatorFn creator(const ClassId& id) const { [[nodiscard]] CreatorFn creator(const ClassId &id) const {
return classes.at(id); return classes.at(id);
} }
@@ -94,14 +94,14 @@ public:
* @brief Creates an instance for the given id using provided arguments. * @brief Creates an instance for the given id using provided arguments.
* @throws std::out_of_range when id is not registered. * @throws std::out_of_range when id is not registered.
*/ */
[[nodiscard]] BasePtr create(const ClassId& id, Args... args) const { [[nodiscard]] BasePtr create(const ClassId &id, Args... args) const {
return creator(id)(std::move(args)...); return creator(id)(std::move(args)...);
} }
/** /**
* @brief Checks whether an id is registered. * @brief Checks whether an id is registered.
*/ */
[[nodiscard]] bool contains(const ClassId& id) const { [[nodiscard]] bool contains(const ClassId &id) const {
return classes.find(id) != classes.end(); return classes.find(id) != classes.end();
} }
@@ -110,9 +110,9 @@ protected:
std::unordered_map<ClassId, CreatorFn> classes; std::unordered_map<ClassId, CreatorFn> classes;
private: private:
template <class Derived> template<class Derived>
static BasePtr instantiate(Args... args) { static BasePtr instantiate(Args... args) {
return std::make_shared<Derived>(std::move(args)...); return std::make_shared<Derived>(std::move(args)...);
} }
}; };
#endif // TYPEFACTORY_H #endif// TYPEFACTORY_H