Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
74 changes: 73 additions & 1 deletion src/apps/middleware/clang_tool/generate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,75 @@ ::clang::ast_matchers::StatementMatcher pubsub_matcher(std::string method)
}
}

// Matches publish_dynamic<Data, scheme>(data, DynamicGroup("...")) and
// subscribe_dynamic<Data, scheme>(callback, DynamicGroup("...")) calls where the
// group name can be statically extracted from the DynamicGroup constructor argument.
::clang::ast_matchers::StatementMatcher pubsub_dynamic_matcher(std::string method)
{
using namespace clang::ast_matchers;

// Same calling thread matcher as in pubsub_matcher
auto calling_thread_matcher = on(expr(
anyOf(
cxxMemberCallExpr(on(hasType(
pointsTo(hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
cxxRecordDecl().bind("on_thread_decl"),
isDerivedFrom(cxxRecordDecl(hasName("::goby::middleware::Thread"))))))))))),
hasDescendant(cxxMemberCallExpr(on(hasType(pointsTo(cxxRecordDecl(
cxxRecordDecl().bind("on_indirect_thread_decl"),
isDerivedFrom(cxxRecordDecl(hasName("::goby::middleware::Thread"))))))))),
expr().bind("on_expr")),
hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
decl().bind("on_type_decl"),
isDerivedFrom(cxxRecordDecl(hasName("::goby::middleware::StaticTransporterInterface"))),
unless(hasName("::goby::middleware::NullTransporter")))))))));

auto containing_class_matcher =
anyOf(hasAncestor(cxxRecordDecl().bind("containing_class_decl")), expr());

// Match a DynamicGroup constructor that contains an extractable string literal.
// DynamicGroup takes const std::string&, so the string literal is nested inside a
// std::string constructor; use hasDescendant to find it.
auto dynamic_group_with_string = cxxConstructExpr(
hasDeclaration(cxxConstructorDecl(
ofClass(cxxRecordDecl(hasName("::goby::middleware::DynamicGroup"))))),
hasDescendant(stringLiteral().bind("group_string_arg")));

// Match a DynamicGroup(uint32_t) constructor with an integer literal argument.
auto dynamic_group_with_int = cxxConstructExpr(
hasDeclaration(cxxConstructorDecl(
ofClass(cxxRecordDecl(hasName("::goby::middleware::DynamicGroup"))))),
hasArgument(0,
anyOf(integerLiteral().bind("group_int_arg"),
declRefExpr(hasDeclaration(
varDecl(hasDescendant(integerLiteral().bind("group_int_arg"))))))));

// The group is function argument 1 for both publish_dynamic and subscribe_dynamic.
// Match either an inline DynamicGroup construction or a variable holding one.
auto group_arg_matcher = hasArgument(
1,
anyOf(
// Inline DynamicGroup("str") or DynamicGroup("str", int) at call site
hasDescendant(dynamic_group_with_string),
// Inline DynamicGroup(int) at call site
hasDescendant(dynamic_group_with_int),
// Variable reference whose declaration is initialized via DynamicGroup("str")
declRefExpr(hasDeclaration(varDecl(hasDescendant(dynamic_group_with_string)))),
// Variable reference whose declaration is initialized via DynamicGroup(int)
declRefExpr(hasDeclaration(varDecl(hasDescendant(dynamic_group_with_int))))));

// publish_dynamic<Data, scheme> / subscribe_dynamic<Data, scheme>:
// template arg 0 = Data type, template arg 1 = scheme (integral)
auto dynamic_method_matcher = callee(cxxMethodDecl(
hasName(method),
hasTemplateArgument(0, templateArgument().bind("type_arg")),
hasTemplateArgument(1, templateArgument(templateArgument().bind("scheme_arg"),
refersToIntegralType(qualType(asString("int")))))));

return cxxMemberCallExpr(expr().bind("pubsub_call_expr"), calling_thread_matcher,
dynamic_method_matcher, group_arg_matcher, containing_class_matcher);
}

class PubSubAggregator : public ::clang::ast_matchers::MatchFinder::MatchCallback
{
public:
Expand Down Expand Up @@ -282,6 +351,7 @@ class PubSubAggregator : public ::clang::ast_matchers::MatchFinder::MatchCallbac

auto method = pubsub_call_expr->getMethodDecl()->getNameAsString();
bool is_regex = (method.find("regex") != std::string::npos);
bool is_dynamic = (method == "publish_dynamic" || method == "subscribe_dynamic");

auto direction = (method.find("publish") != std::string::npos)
? goby::clang::PubSubEntry::Direction::PUBLISH
Expand All @@ -307,7 +377,7 @@ class PubSubAggregator : public ::clang::ast_matchers::MatchFinder::MatchCallbac
return;

entries_.emplace(layer, direction, thread, group, scheme, type, thread_is_known, necessity,
is_regex);
is_regex, is_dynamic);
}

const std::set<PubSubEntry>& entries() const { return entries_; }
Expand Down Expand Up @@ -364,6 +434,8 @@ int goby::clang::generate(::clang::tooling::ClangTool& Tool, std::string output_
finder.addMatcher(pubsub_matcher("publish"), &publish_aggregator);
finder.addMatcher(pubsub_matcher("subscribe"), &subscribe_aggregator);
finder.addMatcher(pubsub_matcher("subscribe_type_regex"), &subscribe_aggregator);
finder.addMatcher(pubsub_dynamic_matcher("publish_dynamic"), &publish_aggregator);
finder.addMatcher(pubsub_dynamic_matcher("subscribe_dynamic"), &subscribe_aggregator);

if (output_file.empty())
output_file = target_name + "_interface.yml";
Expand Down
13 changes: 11 additions & 2 deletions src/apps/middleware/clang_tool/pubsub_entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,15 @@ struct PubSubEntry
if (is_regex_node && is_regex_node.as<bool>())
is_regex = true;

auto is_dynamic_node = yaml["is_dynamic"];
if (is_dynamic_node && is_dynamic_node.as<bool>())
is_dynamic = true;

init();
}

PubSubEntry(Layer l, Direction d, std::string th, std::string g, std::string s, std::string t,
bool tk, goby::middleware::Necessity n, bool r)
bool tk, goby::middleware::Necessity n, bool r, bool dynamic = false)
: layer(l),
direction(d),
thread(th),
Expand All @@ -99,7 +103,8 @@ struct PubSubEntry
type(t),
thread_is_known(tk),
necessity(n),
is_regex(r)
is_regex(r),
is_dynamic(dynamic)
{
init();
}
Expand Down Expand Up @@ -137,6 +142,7 @@ struct PubSubEntry
bool thread_is_known{true};
goby::middleware::Necessity necessity{goby::middleware::Necessity::OPTIONAL};
bool is_regex{false};
bool is_dynamic{false};
bool is_inner_pub{false};
int publish_index{-1};

Expand Down Expand Up @@ -179,6 +185,9 @@ struct PubSubEntry

if (is_regex)
entry_map.add("is_regex", "true");

if (is_dynamic)
entry_map.add("is_dynamic", "true");
}

std::string as_string(goby::middleware::Necessity n) const
Expand Down
20 changes: 15 additions & 5 deletions src/apps/middleware/clang_tool/visualize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ const auto required_style = "bold";
const auto recommended_style = "tapered";
const auto optional_style = "solid";
const auto regex_style = "dotted";
const auto dynamic_style = "dashed";

void escape_for_dot(std::string& s)
{
Expand All @@ -476,7 +477,8 @@ std::string node_name(std::string p, std::string m, std::string a, std::string t

std::string connection_with_label_final(const PubSubEntry& pub, std::string pub_str,
std::string sub_str, std::string color,
goby::middleware::Necessity necessity, bool is_regex)
goby::middleware::Necessity necessity, bool is_regex,
bool is_dynamic = false)
{
g_pubs_in_use[pub_str][pub.layer].insert(std::make_pair(pub.publish_index, pub));

Expand All @@ -503,13 +505,20 @@ std::string connection_with_label_final(const PubSubEntry& pub, std::string pub_
if (is_regex)
style = regex_style;

// Dynamic groups use dashed style; note that is_regex and is_dynamic are mutually
// exclusive in practice (there is no subscribe_type_regex_dynamic method).
if (is_dynamic)
style = dynamic_style;

// return pub_str + "->" + sub_str + "[xlabel=<<b><font point-size=\"10\">" + group +
// "</font></b><br/><font point-size=\"6\">" + scheme +
// "</font><br/><font point-size=\"8\">" + type + "</font>>" + ",color=" + color +
// ",style=" + style + "]\n";

auto label = pub.publish_index_str();
auto tooltip = label + ": " + pub.group + " | " + pub.scheme + " | " + pub.type;
if (is_dynamic)
tooltip += " [dynamic]";
auto ret = pub_str + "->" + sub_str + "[fontsize=7,headlabel=\"" + label + "\",taillabel=\"" +
label + "\",xlabel=<" + label + "[" + group_without_namespace + "]>,color=\"" +
color + "\",style=" + style + ",tooltip=\"" + tooltip + "\",labeltooltip=\"" +
Expand All @@ -529,7 +538,7 @@ std::string connection_with_label(std::string pub_platform, std::string pub_modu
return connection_with_label_final(
pub, node_name(pub_platform, pub_module, pub_application, pub.thread),
node_name(sub_platform, sub_module, sub_application, sub.thread), color, necessity,
is_regex);
is_regex, pub.is_dynamic || sub.is_dynamic);
}

std::string disconnected_publication(std::string pub_platform, std::string pub_module,
Expand All @@ -552,7 +561,7 @@ std::string disconnected_publication(std::string pub_platform, std::string pub_m
pub, node_name(pub_platform, pub_module, pub_application, pub.thread),
node_name(pub_platform, pub_module, pub_application, pub.thread) +
"_no_subscribers_" + esccolor,
color, goby::middleware::Necessity::OPTIONAL, false);
color, goby::middleware::Necessity::OPTIONAL, false, pub.is_dynamic);
}

std::string disconnected_subscription(std::string sub_platform, std::string sub_module,
Expand All @@ -575,7 +584,8 @@ std::string disconnected_subscription(std::string sub_platform, std::string sub_
g_node_name_to_thread[pub_node] = std::make_shared<viz::Thread>();

PubSubEntry fake_pub(sub.layer, PubSubEntry::Direction::PUBLISH, sub.thread, sub.group,
sub.scheme, sub.type, sub.thread_is_known, sub.necessity, sub.is_regex);
sub.scheme, sub.type, sub.thread_is_known, sub.necessity, sub.is_regex,
sub.is_dynamic);

g_pubs_in_use[pub_node][sub.layer].insert(std::make_pair(fake_pub.publish_index, fake_pub));

Expand All @@ -586,7 +596,7 @@ std::string disconnected_subscription(std::string sub_platform, std::string sub_
node_name(sub_platform, sub_module, sub_application, sub.thread) +
"_no_publishers_" + esccolor,
node_name(sub_platform, sub_module, sub_application, sub.thread), color, necessity,
is_regex);
is_regex, sub.is_dynamic);
}

void write_thread_connections(std::ofstream& ofs, const viz::Platform& platform,
Expand Down