cppfront icon indicating copy to clipboard operation
cppfront copied to clipboard

[SUGGESTION] can we add a way to use attributes?

Open farmerpiki opened this issue 1 year ago • 20 comments

I am not aware of any way to hint the branch predictor in the new syntax

I'd like to be able to use attributes, example cpp1 code:

if(condition) [[likely]] {
 ...
} else {
 ...
}

farmerpiki avatar Aug 21 '24 12:08 farmerpiki

Thanks for the suggestion! I haven't implemented attributes yet, but intend to.

hsutter avatar Aug 21 '24 17:08 hsutter

There might be some overlap with meta-functions and attributes. Maybe C++ attributes could be implemented as meta functions if they are allowed on if statements, expressions etc.

if condition @likely {
  // ...
} else {
  // ...
}

// or maybe it should be before the if
@likely if condition {
  // ...
} else {
  // ...
}

the meta-function would simply lower to

if(condition) [[likely]] {
 // ...
} else {
 // ...
}

zaucy avatar Aug 21 '24 18:08 zaucy

What do you envision the meta function looking like in the case of @likely?

On 21 August 2024 19:30:42 Ezekiel Warren @.***> wrote:

There might be some overlap with meta-functions and attributes. Maybe C++ attributes could be implemented as meta functions if they are allowed on if statements, expressions etc.

if condition @likely { // ... } else { // ... }

// or maybe it should be before the if @likely if condition { // ... } else { // ... }

— Reply to this email directly, view it on GitHubhttps://github.com/hsutter/cppfront/issues/1252#issuecomment-2302712297, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AALUZQNZX5S5ITIAOIQO5Z3ZSTME5AVCNFSM6AAAAABM34W6S6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGMBSG4YTEMRZG4. You are receiving this because you are subscribed to this thread.Message ID: @.***>

SebastianTroy avatar Aug 22 '24 06:08 SebastianTroy

perhaps compiler_services could expose an "add/prepend cpp1" so the likely metafunction could look something like this:

likely: (s: cpp2::meta::statement) = {
  if s.is_if_statement() {
    s.append_cpp1("[[likely]]");
  } else {
    // TODO: add likely for other branches
    error("cannot add likely to non-if statement");
  }
}

zaucy avatar Aug 22 '24 07:08 zaucy

perhaps compiler_services could expose an "add/prepend cpp1" so the likely metafunction could look something like this:

likely: (s: cpp2::meta::statement) = {
  if s.is_if_statement() {
    s.append_cpp1("[[likely]]");
  } else {
    // TODO: add likely for other branches
    error("cannot add likely to non-if statement");

uhm yes you can, you can add [[likely]] to switch statements(this just tells the compiler to test for those marked with likely first, it would probably work the same if you put them first in your code, but it might make it harder for a human to reason about, for example if 4 and 7 are the most likely cases for an int... and you put them first it might seem to people reading the code that some cases were skipped). In many cases you should add [[unlikely]] to the default switch case

farmerpiki avatar Aug 22 '24 07:08 farmerpiki

we actually should have both likely and unlikely ... likely means prioritize this case and unlikely means put this code in another bucket because it might never get called and it allows for keeping the happy flow closer together in cache so that when all goes well it goes faster

farmerpiki avatar Aug 22 '24 07:08 farmerpiki

this suggestion was meant to be more generic though... I want to be able to add attributes to functions as well... and a way to add them so that it just works no matter the compiler or attribute I want to be able to use

[[gnu::target_clones("default", "sse4.2", "avx", "avx2")]]

and others as well

farmerpiki avatar Aug 22 '24 07:08 farmerpiki

Using metafunctions to add attributes makes a lot of sense IMO:

  • it fits nicely in the "reducing the concept count" and "simplification through generalization" principles
  • attributes can become a library feature rather than a core language thing, which enables the use of non-standard (user-defined or compiler-dependant) ones for special purposes

dutkalex avatar Aug 22 '24 12:08 dutkalex

When all you have is a hammer, everything looks like a nail. I personally don't believe metafunctions / metaprograms are the right approach for attributes, as they are supposed to be hints for the implementation, rather than having big impact on the program you are writing (at least that's my perception).

DyXel avatar Aug 22 '24 12:08 DyXel

I agree that it makes sense to distinguish things that are just optimization hints which can be ignored from metafunctions which offer guarantees. However, coming from an HPC background, I have observed that:

  • people tend to rely on certain optimizations actually being implemented (I know that this is not really a good argument to guide language design but non-experts tend to think that if "it's written then it's guaranteed" and I think it is worth taking that into account)
  • in order to leverage compiler-specific attributes and attribute-like language extensions (__device__ annotations for gpu programming for example) in a portable way, the answer is almost always to use macros because there is no other way to insert logic before stamping a function with an attribute. Cpp2 offers a natural alternative with metafunctions, which can encapsulate the raw attributes into solid domain specific abstractions. I believe that in this context most code will never use raw attributes and instead rely on more expressive metafunctions, hence the idea that maybe attributes should simply be primitive metafunctions.

dutkalex avatar Aug 22 '24 13:08 dutkalex

As always, the right answer is probably somewhere in the middle. If you are relying on a certain optimization (e.g. loop unrolling), it might be a better idea to write a little reflection tool that does that for you automatically so you get a stronger guarantee, and attributes remain for hinting the implementation. In this case I would say we need both and that they are orthogonal. I am curious what Herb thinks about completely subsuming attributes as part of the metafunction syntax, its an interesting take for sure.

DyXel avatar Aug 22 '24 13:08 DyXel

The line between what is just an optimization hint and what should have first class support can be blurry. For example, one can argue that loop unrolling is just an optimization and that this should be left to the compiler (maybe with the ability to add an attribute as a hint). But this currently leads to loads of TMP hacks to achieve the same purpose with guarantees. I personally have in my codebase something along these lines for this exact purpose, because it extends the expressive power of the language:

static_for< 0, N >( [&]( auto i ){
  some_regular_function_call( i.value );
  some_template_function_call< i.value >();
} );

EDIT: it seems like you have mind-reading abilities @DyXel 😅

dutkalex avatar Aug 22 '24 13:08 dutkalex

I don't see how a library can implement some of these features, at some point there must be some indication to the compiler, an [[attribute]] so to speak, is burying that in a meta function better than declaring it upfront?

On 22 August 2024 13:25:07 Alex Dutka @.***> wrote:

Using metafunctions to add attributes makes a lot of sense IMO:

  • it fits nicely in the "reducing the concept count" and "simplification through generalization" principles
  • attributes can become a library feature rather than a core language thing, which enables the use of non-standard (user-defined or compiler-dependant) ones for special purposes

— Reply to this email directly, view it on GitHubhttps://github.com/hsutter/cppfront/issues/1252#issuecomment-2304540707, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AALUZQJAYRPWG2BTYIYSV4TZSXKB7AVCNFSM6AAAAABM34W6S6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGMBUGU2DANZQG4. You are receiving this because you commented.Message ID: @.***>

SebastianTroy avatar Aug 22 '24 14:08 SebastianTroy

I don't see how a library can implement some of these features

I don't see why we must have an attribute syntax in cpp2 to emit cpp1 code with attributes. Am I missing something there?

dutkalex avatar Aug 22 '24 14:08 dutkalex

Conceptually, at some point, there must be something that the transpiler, reads and understands as an attribute, rather than code. I don't believe that reserving various meta function names to replace attributes is a good course of action.

On 22 August 2024 15:46:15 Alex Dutka @.***> wrote:

I don't see how a library can implement some of these features

I don't see why we must have attributes in cpp2 to emit cpp1 code with attributes. Am I missing something there?

— Reply to this email directly, view it on GitHubhttps://github.com/hsutter/cppfront/issues/1252#issuecomment-2304858467, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AALUZQKXW3FOJAHGLATM3ATZSX2THAVCNFSM6AAAAABM34W6S6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGMBUHA2TQNBWG4. You are receiving this because you commented.Message ID: @.***>

SebastianTroy avatar Aug 22 '24 15:08 SebastianTroy

not to detract from C++ attributes too much here, but to expand the idea of "emitting cpp1" in a metafunction (which would cover C++ attributes) - the unreal engine has a sort of "attribute" that they decorate their classes and functions with that I think having an "emitting cpp1" feature could cover.

for instance you could write an unreal engine class in cpp2 like this:

AMyActor: @uclass type = {
  this: AActor;
  // ...
};

that would lower to this

UCLASS()
class AMyActor : public AActor {
  GENERATED_BODY()
};

zaucy avatar Aug 22 '24 17:08 zaucy

Ideally, the metaclass would expand to whatever the macro expands to, not to the macro itself. Same idea with Q_OBJECT in Qt. Herb touched on this in one of his talks, I don't remember exactly which one though 😅

DyXel avatar Aug 22 '24 17:08 DyXel

Ideally, the metaclass would expand to whatever the macro expands to, not to the macro itself

Totally agree, but it would be a really nice stepping stone. Especially for the unreal example above since if it could emit what I described it could be used today since it could use existing tools (unreal header tool, unreal build tool, etc.)

zaucy avatar Aug 22 '24 18:08 zaucy

I've been waiting on [[no_unique_address]].

I also use this to have [[no_unique_address]] members:

#define QTY_NAME_MEM_TYPE [[no_unique_address]] name_type

-- https://github.com/hsutter/cppfront/discussions/658#discussioncomment-6899974

JohelEGP avatar Oct 06 '24 19:10 JohelEGP

I agree that we shouldn't abuse metafunctions.

The likes of @java_interface and @qt::moc are not only fine, they are one of the intended audience of metafunctions. Even if they don't modify the type, but only read it to write some output.

There are other features we don't have in Cpp2 yet. These aren't Cpp1 attributes, but could be expressed in their Cpp2 syntax or as metafunctions.

JohelEGP avatar Oct 10 '24 15:10 JohelEGP