[cfe-dev] Proposal: parsing Qt signals/slots with attributes
Douglas Gregor
dgregor at apple.com
Tue Sep 27 14:27:26 PDT 2011
Hi Erik,
On Sep 20, 2011, at 1:10 AM, erikjv wrote:
> Hello,
>
> I would like to add support for recognising Qt's signal/slot mechanism to clang by using attributes. For that, I have a proposal for a few small changes to the clang parser. These changes use the attributes to mark AST nodes as signal/slot/invokable, and expose those attributes through libclang.
>
> A bit of background: if you are more familiar with Objective-C, then the signal/slot mechanism works nearly exactly the same as Cocoa's IBAction/IBOutlet: it uses a meta-type system to invoke the methods, or let slots connected to a signal know that the signal was emitted. Qt's meta-type system also allows for "invokable methods", which can be called/invoked without being tagged as slot.
>
> Example:
>
> class Test {
> public:
> Q_INVOKABLE void invokableA();
>
> public slots:
> void slotA();
> void inlineSlotB() {}
>
> signals:
> void signalC();
>
> private:
> Q_SIGNAL void signalD(), signalE();
> Q_SLOT void slotF(), slotG();
> void not_a_slot_or_slot();
> Q_SLOT void inlineSlotH() {}
> };
>
> (To be used properly with Qt, the class would also have to inherit from QObject, and have the Q_OBJECT macro right after the opening brace.)
>
> Taking a leaf from Cocoa's book, we can use the pre-processor to inject:
>
> #define signals protected __attribute__((q_signal))
> #define slots __attribute__((q_slot))
> #define Q_SIGNAL __attribute__((q_signal))
> #define Q_SLOT __attribute__((q_slot))
> #define Q_INVOKABLE __attribute__((q_invokable))
>
> Then the proposed change to clang is to handle it as follows:
> - for method declarations or inline method definitions, accept the q_signal/q_slot/q_invokable attributes, and add the attribute to the AST node
> - for C++ access specifiers, accept an additional (trailing) q_* attribute, and add the attribute to all AST nodes for methods until the next specifier
>
> This is equivalent to how moc (Qt's Meta-Object Compiler) generates the meta-object for a QObject.
>
> Attached is a proof-of-concept implementation/patch (with the above example as test-case). It also adds the attributes as cursor kinds to libclang, so a client application (like Qt Creator) can use it to find out which of the methods are "tagged" as a signal/slot/invokable
I think this is a great direction. Qt is clearly important in the open-source community, and there are a number of technologies (Qt Creator and Qt MOC, at least) that would benefit from better Clang support, and Clang in turn would benefit from the involvement of the Qt community.
A few comments on the patch itself:
diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td
index 4d9aa5b..e6f4119 100644
--- a/include/clang/Basic/Attr.td
+++ b/include/clang/Basic/Attr.td
@@ -647,3 +647,15 @@ def SharedLocksRequired : InheritableAttr {
let Args = [VariadicExprArgument<"Args">];
let LateParsed = 1;
}
+
+def QInvokable : InheritableAttr {
+ let Spellings = ["q_invokable"];
+}
+
+def QSignal : InheritableAttr {
+ let Spellings = ["q_signal"];
+}
+
+def QSlot : InheritableAttr {
+ let Spellings = ["q_slot"];
+}
Why the "q_" prefer rather than "qt_"? Is it because of the Q_-prefixed macros? I ask because "qt_" seems more descriptive.
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index d32c36e..85ad602 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -1047,7 +1047,8 @@ private:
void DeallocateParsedClasses(ParsingClass *Class);
void PopParsingClass(Sema::ParsingClassState);
- Decl *ParseCXXInlineMethodDef(AccessSpecifier AS, ParsingDeclarator &D,
+ Decl *ParseCXXInlineMethodDef(AccessSpecifier AS, AttributeList *QtAttrs,
+ ParsingDeclarator &D,
const ParsedTemplateInfo &TemplateInfo,
const VirtSpecifiers& VS, ExprResult& Init);
I'd rather call this "AccessAttrs" rather than "QtAttrs". Open up a new place for people to add attributes, and they'll find new (non-Qt-related) reasons to add attributes there :)
@@ -2131,10 +2138,19 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
CurAS = AS;
SourceLocation ASLoc = Tok.getLocation();
ConsumeToken();
- if (Tok.is(tok::colon))
- Actions.ActOnAccessSpecifier(AS, ASLoc, Tok.getLocation());
- else
+
+ xsAttrs.clear();
+ MaybeParseGNUAttributes(xsAttrs);
+
+ if (Tok.is(tok::colon)) {
+ Decl *xsDecl = Actions.ActOnAccessSpecifier(AS, ASLoc,
+ Tok.getLocation());
+ if (AttributeList *Attrs = xsAttrs.getList())
+ Actions.ProcessDeclAttributeList(Actions.CurScope, xsDecl, Attrs,
+ false, true);
+ } else {
Diag(Tok, diag::err_expected_colon);
+ }
Could we do some more validation here, so that we reject all attributes *except* those explicitly marked as being okay for access specifiers?
Otherwise, looks good!
- Doug
More information about the cfe-dev
mailing list