__attribute__((attr-list)) or [[attr-list]]
#pragma is a preprocessor directive; hence, apart from being ugly (for me at least), it puts limitations on its usage. For example, pragma cannot be used inside substitution pattern of a #define (of a ??; what is the correct technical term?).
If something like the __attribute__(()) in GCC (or __declspec in Microsoft; not symmetric and not as readable as the other two in my opinion) is added, this would get rid of those limitations and also improve readability of the code.
Design ideas
Basic Examples:
Some the attributes used in this section do not exist in the current compiler. I have used them to just present the possible syntax so that viewers can get an idea of how it would look when used in code (some cases such as statement attributes do not have any options that could be used in the current version of the compiler but they could be added in the future so let's consider those as well).
-
function attributes
stock __attribute__((naked)) myfunc () { }stock myfunc () __attribute__((naked)) { } -
function argument attributes
stock myfunc(__attribute__((unused)) myarg, normal_arg) { }stock myfunc(myarg __attribute__((unused)) , normal_arg) { } -
variable attributes
new __attribute__((unused)) var;new var __attribute__((unused)); -
label attributes
__attribute__((omit-defaults)) mylabel:(potential solution for a part of #266 )mylabel: __attribute__((omit-default)); -
enumerator attributes
enum { __attribute__((deprecated)) score, points __attribute__((deprecated)), }; enum __attribute__((unused)) { } enum [[unused]] { } -
statement attributes
__attribute__((nodestruct)) return 0;
Empty attributes:
stock __attribute__(()) myfunc() { }
new __attribute__(()) myvar;
This might provide more flexibility when attributes are a part of substitution pattern.
Trailing/leading commas:
stock __attribute__((deprecated,)) myfunc() { }
new __attribute__((,used)) myvar;
This might provide more flexibility when attributes are a part of substitution pattern.
Multiple attribute lists:
stock __attribute__((naked)) __attribute__((deprecated)) myfunc () {}
Multiple attributes in a single attributes list:
stock __attribute__((naked, deprecated)) myfunc() {}
Attribute parameters:
stock __attribute__((naked(32))) myfunc () {}
stock __attribute__((deprecated("because bla"))) myfunc () {}
Changes:
- removed
[[]]syntax-
Were this done, attribute or another function-like macro would be VASTLY preferable to [[]] for backwards-compatibility. It would be very easy to strip them in the old compiler with:
#define __attribute__(%0)Removing [[]] in a conditional manner would be nigh-on impossible.
-
https://github.com/pawn-lang/YSI-Includes/blob/d31e1916454e984bc927eee2102532ed25d5fa77/YSI_Internal/y_distribute.inc#L111-L130
https://github.com/pawn-lang/YSI-Includes/blob/b2993bdaed33642c3c48f94ed063eb84ef4c91f7/YSI_Visual/y_commands/impl.inc#L145-L148
Were this done, __attribute__ or another function-like macro would be VASTLY preferable to [[]] for backwards-compatibility. It would be very easy to strip them in the old compiler with:
#define __attribute__(%0)
Removing [[]] in a conditional manner would be nigh-on impossible.
There is also __pragma in MSVC (I don't know about others), which would be good for consistency in the same way as #emit/__emit() mirror each other.
I'm up for __pragma as much as I like the __attribute__ syntax/name I agree with Y_Less on the #emit/__emit() point.
This looks like a promising idea though! Being able to inline these will be far superior to the directive method.
Actually, it should really be __Pragma which would match the other new compiler symbols __Pawn and __PawnBuild.
Not that I like that naming scheme...
There are no operators named in the CamelCase style yet, do we really have to make one?
Regarding __PawnBuild, it was obviously named for consistency with __Pawn, and __Pawn is not even a new symbol, it's from the original compiler and was named that way for who knows what reason (maybe because the original Pawn author thought the name of that constant should start from a capital letter if it contains the language name?)
The point is, even if constants in CamelCase exist in the compiler, it doesn't mean we have to name the other new things in the same style.
That's pretty much exactly what that means. Things in the compiler should be consistent, not named a dozen different ways.
Though I did mention somewhere else that as long as there is consistency, maybe __PawnBuild could be changed instead.
Plus the fact that __Pawn was in the original compiler only adds to its use as precedent.
-
Do we have enough consensus for this to be implemented?
-
Should every option that is available through
__TBDbe made available through#pragmaand vice-versa? (TBD = to be decided) -
What options must be provided?
-
Yes! This is really useful for library writers. The attributes can be placed inside substitution patterns of
#defines. -
In my opinion, we could keep these two features entirely separate. The
#pragmais used to modify general compiler settings such as compatibility mode, rational number support, etc. and this new method be used for attributes that actually affect symbols. -
#pragmaoptions__TBDoptionsoption deprecated compat unused rational nodestruct amxlimit naked amxram codepage ctrlchar compress dynamic library overlay pack semicolon tabsize The
deprecated,unusedandnakedoptions will remain with#pragmafor backward compatibility. The new attributes such asnodestructcould be supported only by__TBD.
I'd like to work on implementing at least some of the attributes, but I have a few questions:
- Do we have a consensus on what keyword to use? If it's
__attribute__, would it be OK to implement it without the trailing underscores (__attribute), or do they have a special meaning? - Is it necessary to wrap the attributes in two pairs of parentheses? Shouldn't one pair be enough? Or maybe we could even make them optional if the user needs to specify only one attribute?
- Now that we have
#pragma unreadand#pragma unwritten, maybe we should have similar attributes as well? - Should we make attribute
unusedapplicable for functions, so its functionality would be similar to thestockspecifier? - Should we warn the user if an attribute is repeated (e.g.
__TBD(unused, unused)or__TBD(deprecated "some text") symbol __TBD(deprecated "other text"))?
Is it necessary to wrap the attributes in two pairs of parentheses? Shouldn't one pair be enough? Or maybe we could even make them optional if the user needs to specify only one attribute?
I picked up the syntax from GCC. It seems the double brackets are there for compatibility with other compilers.
Should we warn the user if an attribute is repeated (e.g. __TBD(unused, unused) or __TBD(deprecated "some text") symbol __TBD(deprecated "other text"))?
Ignoring would offer more flexibility while writing preprocessor code.
OK, I see no reason why this should be different to #pragma; aAll the talk about what should be available to which is just IMHO a pointless complication. Or at the very least make the options available to __TBD a strict subset of those available to #pragma, because @YashasSamaga is right about some only being needed once at the top, but there's no reason not to keep #pragma as an option for the rest. #pragma is still the main canonical way to do things, and so should retain full capabilities, with this extension available mainly for advanced use cases like libraries.
As to syntax, I'd propose:
__Pragma("options here")
For several reasons:
-
Basing it on a hack in another language to enable backwards compatibility with obsolete compilers for said other language is just right out as an option IMHO.
-
This is now a first-class part of the language, unlike
#pragma, so it should conform to all the rules of pawn as much as possible. Doing__Pragma(unused a)is just brand new syntax for one tiny use-case, and then you get all the questions about how those contents themselves interact with macros. Putting the commands in a string totally removes questions about how the two different languages (pawn and pragma commands) interact. Basically, quasi-quoting. This also means that the existing#pragmacode can be reused in it's entirety on the finally parsed string. -
__Pragma(or__pragma) fits with other conventions in the language -#emitand__emit,__Pawn, and more.
This keeps the two systems as close as possible, which helps with coding, testing, teaching, and learning. Making them very different IMHO serves no purpose and just complicates things as then you have to go out of your way to explain the differences, and explain why expressions in the ()s don't match expressions anywhere else in the language.
I picked up the syntax from GCC. It seems the double brackets are there for compatibility with other compilers.
Then this shouldn't be a problem for us, as Pawn macros work in a different way and we can easily define a compatibility macro like this:
#define __TBD(%0)
OK, I see no reason why this should be different to
#pragma; aAll the talk about what should be available to which is just IMHO a pointless complication.
So, in other words, all repeated options, attributes being inapplicable to certain kinds of symbols (e.g. naked is only applicable to functions, but not variables, arrays or constants), and any other kinds of errors should be silently ignored in __TBD? Did I understand you correctly?
My main concern was that originally Pawn was positioned as a safe language with maximum control over errors. But on the other hand, this is not the original Pawn...
Or at the very least make the options available to
__TBDa strict subset of those available to#pragma, because @YashasSamaga is right about some only being needed once at the top, but there's no reason not to keep#pragmaas an option for the rest.
I never said anything about __TBD having all options from #pragma. Or was this about me suggesting the inclusion of unread and unwritten into the list of available options, and you meant that the list should consist only from what @YashasSamaga posted 2 years ago (deprecated, unused, nodestruct, naked)? If not, then what is the exact list of options that you see fitting for __TBD?
Putting the commands in a string totally removes questions about how the two different languages (pawn and pragma commands) interact.
... but brings other questions, such as:
- What should we use as delimiters for attributes - commas with optional whitespaces before/after them, as proposed initially
__TBD("unused, nodestruct")
, or just whitespaces are enough?
__TBD("unused nodestruct")
- How are we going to handle the string arguments for attributes (f.e., the deprecation message)? Like this:
__TBD("deprecated \"- please use OtherFunction() instead\"")
, this:
__TBD("deprecated(- please use OtherFunction() instead)")
, or this (combined):
__TBD("deprecated(\"- please use OtherFunction() instead\")")
?
__Pragma(or__pragma) fits with other conventions in the language
So you mean it's alright if I implement the keyword as __pragma, in lowercase? Because I see no reason why __Pragma would be better, especially since currently we have no operators named in CamelCase.
What should we use as delimiters for attributes - commas with optional whitespaces before/after them, as proposed initially
Why would you delimit attributes? You can't do that with #pragma:
#pragma unused, nodestruct
How are we going to handle the string arguments for attributes (f.e., the deprecation message)? Like this:
Again, same as in #pragma:
#pragma deprecated doesn't need quotes
especially since currently we have no operators named in CamelCase.
__Pawn, __PawnBuild. But they are they only two, so probably the outliers rather than the pattern.