Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
67648ce
Fixed broken const-correctness in dbc.hpp and dbc.cpp.
SimonCahill Aug 15, 2025
0fd65cd
Added more const-correctness fixes.
SimonCahill Aug 15, 2025
8c70c63
Removed String::trim function, in favour in trim template functions
SimonCahill Aug 15, 2025
214d652
Fixed broken logic in get_line function.
SimonCahill Aug 15, 2025
3d6e710
Replaced String::trim function with generic trim functions which work…
SimonCahill Aug 15, 2025
3abbf0c
Test directly uses trim template
SimonCahill Aug 15, 2025
01747c7
Added <algorithm> header.
SimonCahill Aug 15, 2025
4b470f1
Logic error in unit test
SimonCahill Aug 15, 2025
ab6fae9
Added isWhitespaceOrEmpty template function
SimonCahill Aug 15, 2025
2094309
Fixed weird logic in `get_next_non_blank_line` and `skip_to_next_blan…
SimonCahill Aug 15, 2025
8f6f31b
Added copyright notices and documentation to added functions
SimonCahill Aug 15, 2025
61f6130
More const correctness fixes.
SimonCahill Aug 15, 2025
32f2dbd
Removed extra `.c_str()` call.
SimonCahill Aug 15, 2025
e281cc8
Added functions to determine whether or not a .dbc file is valid or not.
SimonCahill Aug 15, 2025
4d23774
Fixed bug reported in "parseSignals should fail if frame data length …
SimonCahill Aug 15, 2025
13ebc8b
Clang doesn't like ntohl (I misread it as being 64 bit); swapped to g…
SimonCahill Aug 15, 2025
601ef66
Kicked out string_view overload
SimonCahill Aug 15, 2025
3f0ec28
Reverted endian little change; somehow there's a difference between h…
SimonCahill Aug 15, 2025
2afd3a1
Added option to enable modern C++ versions.
SimonCahill Aug 19, 2025
a3e3323
Refactored code; fixed tests.
SimonCahill Aug 19, 2025
fa8ffb9
Update CMakeLists.txt
SimonCahill Sep 11, 2025
d30508f
Removed copyright
SimonCahill Sep 11, 2025
5b34335
Merge branch 'fix/const-correctness' of github.com:SimonCahill/dbc_pa…
SimonCahill Sep 11, 2025
22a6412
Updated implementation of to not return blank line, but last known g…
SimonCahill Sep 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ project(dbc VERSION 0.5.0 DESCRIPTION "C++ DBC Parser")

# -- PROJECT OPTIONS -- #
option(DBC_ENABLE_TESTS "Enable Unittests" ON)
option(DBC_MODERN_CPP "Enable support for modern C++ 17. Default is C++ 11 for legacy systems." OFF)
option(DBC_TEST_LOCALE_INDEPENDENCE "Used to deterime if the libary is locale agnostic when it comes to converting floats. You need `de_DE.UTF-8` locale installed for this testing." OFF)
option(DBC_GENERATE_DOCS "Use doxygen if installed to generated documentation files" OFF)
option(DBC_GENERATE_SINGLE_HEADER "This will run the generator for the single header file version. Default is OFF since we make a static build. Requires cargo installed." OFF)
Expand All @@ -21,8 +22,13 @@ set(CPACK_RESOURCE_FILE_README ${CMAKE_CURRENT_SOURCE_DIR}/README.md)
include(CPack)

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
if (DBC_ENABLE_TESTS OR DBC_MODERN_CPP)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
else()
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
endif()

find_package(FastFloat QUIET)
if (NOT ${FastFloat_FOUND})
Expand Down
29 changes: 20 additions & 9 deletions include/libdbc/dbc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
#include <string>
#include <vector>

#if __cplusplus >= 201703L
#include <filesystem>
#endif // __cplusplus >= 201703L

namespace Libdbc {

class Parser {
Expand All @@ -27,18 +31,25 @@ class DbcParser : public Parser {
void parse_file(const std::string& file_name) override;
void parse_file(std::istream& stream) override;

std::string get_version() const;
std::vector<std::string> get_nodes() const;
std::vector<Libdbc::Message> get_messages() const;
const std::string& get_version() const;
const std::vector<std::string>& get_nodes() const;
const std::vector<Libdbc::Message>& get_messages() const;

Message::ParseSignalsStatus parse_message(uint32_t message_id, const std::vector<uint8_t>& data, std::vector<double>& out_values) const;

Message::ParseSignalsStatus parse_message(uint32_t message_id, const std::vector<uint8_t>& data, std::vector<double>& out_values);
const std::vector<std::string>& unused_lines() const;

std::vector<std::string> unused_lines() const;
#if __cplusplus >= 201703L
static void validate_dbc_file(const std::filesystem::path&);
#else
static void validate_dbc_file(const std::string&);
#endif // __cplusplus >= 201703L
static void validate_dbc_file(std::istream& stream);

private:
std::string version;
std::vector<std::string> nodes;
std::vector<Libdbc::Message> messages;
std::string version{};
std::vector<std::string> nodes{};
std::vector<Libdbc::Message> messages{};

std::regex version_re;
std::regex bit_timing_re;
Expand All @@ -48,7 +59,7 @@ class DbcParser : public Parser {
std::regex value_re;
std::regex signal_re;

std::vector<std::string> missed_lines;
std::vector<std::string> missed_lines{};

void parse_dbc_header(std::istream& file_stream);
void parse_dbc_nodes(std::istream& file_stream);
Expand Down
38 changes: 12 additions & 26 deletions include/libdbc/exceptions/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ class ValidityError : public Exception {

class NonDbcFileFormatError : public ValidityError {
public:
NonDbcFileFormatError(const std::string& path, const std::string& extension) {
error_msg = {"File is not of DBC format. Expected a .dbc extension. Cannot read this type of file (" + path + "). Found the extension (" + extension
+ ")."};
}
explicit NonDbcFileFormatError(const std::string& path, const std::string& extension):
error_msg("File is not of DBC format. Expected a .dbc extension. Cannot read this type of file ("
+ path + "). Found the extension (" + extension + ")."
) { }
explicit NonDbcFileFormatError(const std::string& error):
error_msg("File is not of DBC format. Cannot read this type of file (" + error + ").") { }

const char* what() const throw() override {
return error_msg.c_str();
Expand All @@ -35,32 +37,16 @@ class NonDbcFileFormatError : public ValidityError {
std::string error_msg;
};

class DbcFileIsMissingVersion : public ValidityError {
class DbcFileIsMissingVersion : public NonDbcFileFormatError {
public:
DbcFileIsMissingVersion(const std::string& line) {
error_msg = {"Invalid dbc file. Missing the required version header. Attempting to read line: (" + line + ")."};
}

const char* what() const throw() override {
return error_msg.c_str();
}

private:
std::string error_msg;
explicit DbcFileIsMissingVersion(const std::string& line):
NonDbcFileFormatError("Invalid dbc file. Missing the required version header. Attempting to read line: (" + line + ").") { }
};

class DbcFileIsMissingBitTiming : public ValidityError {
class DbcFileIsMissingBitTiming : public NonDbcFileFormatError {
public:
DbcFileIsMissingBitTiming(const std::string& line) {
error_msg = {"Invalid dbc file. Missing required bit timing in the header. Attempting to read line: (" + line + ")."};
}

const char* what() const throw() override {
return error_msg.c_str();
}

private:
std::string error_msg;
explicit DbcFileIsMissingBitTiming(const std::string& line):
NonDbcFileFormatError("Invalid dbc file. Missing required bit timing in the header. Attempting to read line: (" + line + ").") { }
};

} // libdbc
Expand Down
13 changes: 7 additions & 6 deletions include/libdbc/message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ struct Message {
ErrorBigEndian,
ErrorUnknownID,
ErrorInvalidConversion,
ErrorInvalidSignalSize,
};

ParseSignalsStatus parse_signals(const std::vector<uint8_t>& data, std::vector<double>& values) const;

void append_signal(const Signal& signal);
std::vector<Signal> get_signals() const;
const std::vector<Signal>& get_signals() const;
uint32_t id() const;
uint8_t size() const;
const std::string& name() const;
Expand All @@ -33,11 +34,11 @@ struct Message {
virtual bool operator==(const Message& rhs) const;

private:
uint32_t m_id;
std::string m_name;
uint8_t m_size;
std::string m_node;
std::vector<Signal> m_signals;
uint32_t m_id{};
std::string m_name{};
uint8_t m_size{};
std::string m_node{};
std::vector<Signal> m_signals{};

friend std::ostream& operator<<(std::ostream& out, const Message& msg);
};
Expand Down
6 changes: 3 additions & 3 deletions include/libdbc/signal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct Signal {

Signal() = delete;
virtual ~Signal() = default;
explicit Signal(std::string name,
explicit Signal(const std::string& name,
bool is_multiplexed,
uint32_t start_bit,
uint32_t size,
Expand All @@ -40,8 +40,8 @@ struct Signal {
double offset,
double min,
double max,
std::string unit,
std::vector<std::string> receivers);
const std::string& unit,
const std::vector<std::string>& receivers);

virtual bool operator==(const Signal& rhs) const;
bool operator<(const Signal& rhs) const;
Expand Down
99 changes: 98 additions & 1 deletion include/libdbc/utils/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@
#ifndef UTILS_HPP
#define UTILS_HPP

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>

#if __cplusplus >= 201703L
# include <string_view>
#endif // __cplusplus >= 201703L

#if __cpp_concepts >= 201907
#include <concepts>
template<typename Str>
concept procsys_stringable = requires(Str s) { { s.data() + s.size() } -> std::convertible_to<const char*>; };
#endif // __cpp_concepts >= 201907

namespace Utils {

class StreamHandler {
Expand All @@ -26,9 +37,95 @@ class StreamHandler {
static std::istream& skip_to_next_blank_line(std::istream& stream, std::string& line);
};

/**
* @brief Trims the specified characters from the beginning and end of the given string.
*
* @tparam T The typeparam; must be stringable.
* @param value The string to trim.
* @return A new string with the specified characters trimmed.
*/
#if __cplusplus >= 201703
# if __cpp_concepts >= 201907
template<procsys_stringable T>
# else
template<typename T>
# endif // __cpp_concepts >= 201907
constexpr auto trim(const T& value) {
#else
template<typename T>
std::string trim(const T& value) {
#endif // #if __cplusplus >= 201703
T trimmedValue = value;
trimmedValue.erase(trimmedValue.begin(), std::find_if(trimmedValue.begin(), trimmedValue.end(), [](int ch) { return !std::isspace(ch); }));
trimmedValue.erase(std::find_if(trimmedValue.rbegin(), trimmedValue.rend(), [](int ch) { return !std::isspace(ch); }).base(), trimmedValue.end());
return trimmedValue;
}

/**
* @brief Trims the specified characters from the beginning and end of the given string.
*
* @tparam T The typeparam; must be stringable.
* @param value The string to trim.
* @param trimChars The characters to trim from the string.
* @return A new string with the specified characters trimmed.
*/
#if __cplusplus >= 201703
# if __cpp_concepts >= 201907
template<procsys_stringable T>
# else
template<typename T>
# endif // __cpp_concepts >= 201907
constexpr auto trim(const T& value, std::initializer_list<char>& trimChars) {
#else
template<typename T>
std::string trim(const T& value, const std::initializer_list<char>& trimChars) {
#endif // __cplusplus >= 201703
T trimmedValue = value;
trimmedValue.erase(trimmedValue.begin(), std::find_if(trimmedValue.begin(), trimmedValue.end(), [&trimChars](int ch) { return std::find(trimChars.begin(), trimChars.end(), static_cast<char>(ch)) == trimChars.end(); }));
trimmedValue.erase(std::find_if(trimmedValue.rbegin(), trimmedValue.rend(), [&trimChars](int ch) { return std::find(trimChars.begin(), trimChars.end(), static_cast<char>(ch)) == trimChars.end(); }).base(), trimmedValue.end());
return trimmedValue;
}

/**
* @brief Checks if the given string is empty or contains only whitespace characters.
*
* @tparam T The typeparam; must be stringable.
*
* @param str The string to check against.
*
* @return true If the string is empty or consists only of whitespace.
* @return false Otherwise.
*/
template<typename T>
constexpr bool isWhitespaceOrEmpty(const T& str) {
return std::all_of(str.begin(), str.end(), [](char c) { return std::isspace(c); });
}

/**
* @brief Swaps the endianness of a multi-byte value.
*
* @tparam T The type of the value.
* @param value The value to swap.
* @return T The value with swapped endianness.
*/
template<typename T>
T swapEndianness(T value) {
T swappedBytes;

char* valuePtr = reinterpret_cast<char*>(&value);
char* swappedPtr = reinterpret_cast<char*>(&swappedBytes);

auto sizeInBytes = sizeof(T);
for (size_t i = 0; i < sizeInBytes; i++) {
swappedPtr[sizeInBytes - 1 - i] = valuePtr[i];
}

return swappedBytes;
}

class String {
public:
static std::string trim(const std::string& line);
static std::string trim(const std::string& line) { return Utils::trim(line); }

template<class Container>
static void split(const std::string& str, Container& cont, char delim = ' ') {
Expand Down
35 changes: 25 additions & 10 deletions src/dbc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,24 @@ DbcParser::DbcParser()
+ whiteSpace + receiverPattern) {
}

#if __cplusplus >= 201703L
void DbcParser::validate_dbc_file(const std::filesystem::path& file_path) {
#else
void DbcParser::validate_dbc_file(const std::string& file_path) {
#endif // __cplusplus >= 201703L

std::ifstream testStream{file_path};

return validate_dbc_file(testStream);
}

void DbcParser::validate_dbc_file(std::istream& stream) {
stream.seekg(0, std::ios::beg);

DbcParser parser{};
parser.parse_dbc_header(stream);
}

void DbcParser::parse_file(std::istream& stream) {
std::string line;
std::vector<std::string> lines;
Expand All @@ -88,12 +106,9 @@ void DbcParser::parse_file(std::istream& stream) {
}

void DbcParser::parse_file(const std::string& file_name) {
auto extension = get_extension(file_name);
if (extension != ".dbc") {
throw NonDbcFileFormatError(file_name, extension);
}
validate_dbc_file(file_name);

std::ifstream stream(file_name.c_str());
std::ifstream stream(file_name);

parse_file(stream);
}
Expand All @@ -107,19 +122,19 @@ std::string DbcParser::get_extension(const std::string& file_name) {
return "";
}

std::string DbcParser::get_version() const {
const std::string& DbcParser::get_version() const {
return version;
}

std::vector<std::string> DbcParser::get_nodes() const {
const std::vector<std::string>& DbcParser::get_nodes() const {
return nodes;
}

std::vector<Libdbc::Message> DbcParser::get_messages() const {
const std::vector<Libdbc::Message>& DbcParser::get_messages() const {
return messages;
}

Message::ParseSignalsStatus DbcParser::parse_message(const uint32_t message_id, const std::vector<uint8_t>& data, std::vector<double>& out_values) {
Message::ParseSignalsStatus DbcParser::parse_message(const uint32_t message_id, const std::vector<uint8_t>& data, std::vector<double>& out_values) const {
for (const auto& message : messages) {
if (message.id() == message_id) {
return message.parse_signals(data, out_values);
Expand Down Expand Up @@ -246,7 +261,7 @@ void DbcParser::parse_dbc_messages(const std::vector<std::string>& lines) {
}
}

std::vector<std::string> DbcParser::unused_lines() const {
const std::vector<std::string>& DbcParser::unused_lines() const {
return missed_lines;
}

Expand Down
Loading
Loading