7#include <unordered_set>
11std::string ModelFileChecker::readContents()
const {
12 std::ifstream file(filePath);
13 if (!file.is_open()) {
14 throw std::runtime_error(
"Cannot open MARTY model file: " + filePath);
17 std::string contents((std::istreambuf_iterator<char>(file)), {});
18 contents.erase(std::remove(contents.begin(), contents.end(),
'\r'), contents.end());
23 const std::string contents = readContents();
24 static const std::regex re(
25 R
"((?:template\s*<[^>]*>\s*)class\s+[A-Za-z_]\w*\s*(?::|\{|$))",
26 std::regex::ECMAScript
28 return std::regex_search(contents, re);
32 auto to_upper = [](std::string s) {
33 std::transform(s.begin(), s.end(), s.begin(), [](
unsigned char c) {
34 return static_cast<char>(std::toupper(c));
38 auto to_lower = [](std::string s) {
39 std::transform(s.begin(), s.end(), s.begin(), [](
unsigned char c) {
40 return static_cast<char>(std::tolower(c));
45 const std::string upper = to_upper(model);
46 const std::string lower = to_lower(model);
48 std::vector<std::string> raw = {
57 std::vector<std::string> out;
58 std::unordered_set<std::string> seen;
59 for (
auto& candidate : raw) {
60 if (!candidate.empty() && seen.insert(candidate).second) {
61 out.emplace_back(std::move(candidate));
67std::string ModelFileChecker::regexEscape(
const std::string& value) {
69 escaped.reserve(value.size() * 2);
70 for (
char c : value) {
72 case '\\':
case '^':
case '$':
case '.':
case '|':
case '?':
73 case '*':
case '+':
case '(':
case ')':
case '[':
case ']':
75 escaped.push_back(
'\\');
84bool ModelFileChecker::hasClassDefinition(
const std::string& contents,
85 const std::string& class_name,
87 const std::string escaped = regexEscape(class_name);
89 const std::regex templated(
90 "(?:^|[\\s;{}])template\\s*<[^>]*>\\s*class\\s+" + escaped + R
"(\b\s*(?:final\s*)?(?::|\{|$))",
91 std::regex::ECMAScript
93 if (std::regex_search(contents, templated)) {
98 const std::regex plain(
99 "(?:^|[\\s;{}])class\\s+" + escaped + R
"(\b\s*(?:final\s*)?(?::|\{|$))",
100 std::regex::ECMAScript
102 if (std::regex_search(contents, plain)) {
111 const std::string contents = readContents();
114 for (
const auto& candidate : candidates) {
115 bool is_template =
false;
116 if (hasClassDefinition(contents, candidate, is_template)) {
121 std::ostringstream oss;
122 for (std::size_t i = 0; i < candidates.size(); ++i) {
126 oss << candidates[i];
129 throw std::runtime_error(
130 "Cannot resolve MARTY model class for mty_model_name='" + model +
131 "' in file '" + filePath +
"'. Tried class names: " + oss.str() +
"."
Declares utilities to detect MARTY model classes in C++ files.
static std::vector< std::string > modelClassCandidates(const std::string &model)
Return the exact candidate sequence used by resolveModelClass.
ModelClassInfo resolveModelClass(const std::string &model) const
Resolve the concrete C++ class name for a user-provided model name.
bool isAnyModelTemplate() const
Tests whether the file contains any MARTY model template class.
ModelFileChecker(const std::string &filePath)
Constructs a checker for a given file path.