[Development] [Feature] Q_INFO: Annotations for classes, methods, properties and enums
Olivier Goffart
olivier at woboq.com
Thu Sep 5 09:09:33 CEST 2013
Hi,
On Wednesday 04 September 2013 22:32:34 Stefan Merettig wrote:
> > I think adding general information to method or properties are a good
> > idea.
> > The current tag system is indeed very limited.
> > But before trying to get something to general, we need to focus on use
> > cases.
>
> Alright, I present to you: libAuth, a authentication framework which
> lets the dev write simple or complex permission rules right in the code. As
> modern business logic demands that code is flexible, there's not a big issue
> with hard-coding certain key values.
>
> // We need to allows access to employees and the daemons.
> Q_INFO("libAuth.allowedGroups" ARGS "admin", "moderators", "staff",
> "daemons")
> Q_INFO("libAuth.db.connection" ARGS "BillingPermissions")
> class BillingService : public QObject {
>
> // Customer support may not use this!
> Q_INFO("libAuth.disallowedGroups" ARGS "support")
> public void depositFunds (int credits);
>
> // Maintenance windows must be respected. Only admins.
> Q_INFO("libAuth.allowedTimeRange" ARGS "20:00", "6:00")
> Q_INFO("libAuth.allowedGroups" ARGS "admin")
> public void startMaintenance ();
>
> };
Thank for your example. It really helps to have real world use case to be
able to design a feature that suits the needs.
For example, i don't see the need for two arguments here:
Q_CLASSINFO("libAuth.allowedGroups", "admin,moderators,staff")
Q_INFO("libAuth.allowedTimeRange", "20:00-6:00")
Rationale: coma separated lists are easy to parse (QString::split)
so you'd do:
QStringList allowed = QString(mobj->info("libAuth.allowedGroup")).split(',');
TimeRange tr = TimeRange::fromString(method.info("libAuth.allowedTimeRange"));
> Another use-case would be to do contract-based programming or what the right
> buzzword for that was. (For people who don't know what that is: You define
> allowed values somewhere where a compiler can error if you don't comply to
> these.) We may not be able to get the compiler to throw, but we could nicely
> combine this into a API which gets exposed to a scripting environment:
>
> Q_INFO("Contract.times" ARGS "0", "99")
> Q_INFO("Contact.itemId.checkSlot" ARGS "checkIfItemIdIsValid")
> bool addItemToCart (int itemId, int times);
>
> This is already a pretty complex use-case. In this example, I say that
> the arguments, which are matched by their name, must follow certain
> "rules". That is, the "times" argument must be between 0 and 99. As the
> check if itemId is pretty complex, we tell the system to instead invoke a
> slot which tests it for us.
This is less like a real example, but again, you can just use a coma separated
list. or some specific appropriate expression.
Q_INFO("Contract", "0 < times < 99 && checkIfItemIdIsValid(itemid)")
> Now try this with a simple key-value store. If we'd not allow multiple
> values, then this would simply not possible without really ugly hacks.
> Period.
> If we would allow multiple values, but only a argument per item .. well,
> that'd create a mess for no good. It at least wouldn't improve readability.
[...]
> So, if a user wants to accept a variable amount of arguments, you want him
> to use "argX", while the internal code counts upwards, and the user of this
> feature has to keep track of the number scheme too? That's two levels of
> unneded complexity with a big mess-up potentional. With the proposed API the
> user can't simply write
> Q_INFO("foo" = "bar")
> Q_INFO("foo" = "baz")
> as that would interfere with the concept of a plain key-value store.
Q_INFO("foo" = "bar,baz")
> Of course you can abuse them to do something "better", but meta programming
> is already strange enough without weird string hacks all over the place. A
> clean solution which is easily extend-able in the future may encourage
> others to use it instead of using those hacks, or not using the current
> system at all because these hacks are so ugly and weird.
>
> What I would be fine with is allowing a alternative syntax (So allowing
> both):
> Q_INFO("<Name>" [ARGS <Value>, ...])
> Q_INFO("<Name>", "<Value>")
Remember also that variadic macro requires C++11 and we don't make it
mandatory yet.
So your macro need to have a fixed number of coma.
> The QMetaInfo class *could* even have a convenience method like
> firstArgument(). I'm totally fine with all of these fixes, but the currently
> available facts don't show me why my approach is or may be harmful.
One of the API principles is:
"Make common tasks easy. Rare tasks should be possible but not the focus.
Solve the specific problem; don't make the solution overly general when this is
not needed."
Most case i see will just be key/value.
For the cases where the value is a list, you can just use QString::split,
that's easy. For all the other case, you will have to have a custom parser
anyway.
> > BTW, All of that can be done currently by abusing Q_CLASSINFO
>
> I also could write a const char* as static class member. That's not too
> far from abusing Q_CLASSINFO for something like this.
That's right. and that's why I believe Q_INFO would be a good thing for
methods. But it still has to be designed properly
> > Notice that we could also re-use the "tag" field of the data array for some
> > purpose (it would mean the old tag or the info count depending on one
> > of the bit or of the revision)
>
> Breaks compatibility. If no one cares, well we could of course re-use that
> field as "pointer" to a data table in the meta-data structure. But that's
> really ugly. And the last thing I want is storing something like
> "Foo=Bar Baz=House" as string-data. That'd be a nightmare to parse. We
> did that in Qt4 for the method signature, we now store every part
> individually. We should not do the same mistake again.
The way I see it: (look for the comments with ** )
static const uint qt_meta_data_FOO[] = {
// content:
8, // revision ** now we upgraded to 8 **
0, // classname
0, 0, // classinfo
2, 14, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
MetaObjectFlags::HasInfo, // flags ** alternative, a flag here **
0, // signalCount
// slots: name, argc, parameters, tag / **or info_count** , flags
1, 2, 24, 1, 0x0a | MethodFlags::HasInfo, //** we can use the flag
4, 1, 32, 0x80000000 | 1, 0x0a, //** or a bit in the tag location
// slots: parameters
QMetaType::Void, QMetaType::QString, 3, // Slot 1
QMetaType::Void, 0x80000000 | 5, 2,
42, 42 // ** Infos comme directly after the parameters **
//(slot 2 starts here) ...
> The only solution
> would be storing JSON instead - If that's a solution I don't know.
JSON could be an option too since moc already has a json parser and binary
json exporter.
--
Olivier
Woboq - Qt services and support - http://woboq.com - http://code.woboq.org
More information about the Development
mailing list